feat: add configuration support to enbas
SUMMARY This commit adds configuration support to enbas. The configuration is stored as a JSON file in the user specified configuration directory. When using enbas for the first time, the user will first need to execute the new init command in order to generate the configuration. Once this has been generated the user can edit the settings to personalise their experience, login to their account and use enbas as normal. For now the configurable settings included in the configuration are as follows: - The path to the credentials file (by default this is set to a file in the same directory as the configuration file). - The path to the cache directory. - The character limit used for line wrapping. - The programs used for integrations such as paging, media viewing, opening URLs, etc. CHANGES - added the new config type. - added the new init executor for generating a new configuration file. - removed the following top level flags in favour of the new configration support. - cache-dir - pager - image-viewer - video-player - max-terminal-width - added a new error type for use when an unknown media attachment ID is specified. - updated the usage function for the executors to support a case where a flagsets has no flags. - update .golangci.yaml to disable some linters
This commit is contained in:
parent
63f0526f39
commit
42251f6df8
30 changed files with 425 additions and 220 deletions
|
@ -31,5 +31,8 @@ linters-settings:
|
||||||
linters:
|
linters:
|
||||||
enable-all: true
|
enable-all: true
|
||||||
disable:
|
disable:
|
||||||
#- json
|
- exhaustivestruct
|
||||||
|
- exhaustruct
|
||||||
|
- gomnd
|
||||||
|
- tagliatelle
|
||||||
fast: false
|
fast: false
|
||||||
|
|
|
@ -10,15 +10,16 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
binaryVersion string
|
binaryVersion string //nolint:gochecknoglobals
|
||||||
buildTime string
|
buildTime string //nolint:gochecknoglobals
|
||||||
goVersion string
|
goVersion string //nolint:gochecknoglobals
|
||||||
gitCommit string
|
gitCommit string //nolint:gochecknoglobals
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -29,22 +30,11 @@ func main() {
|
||||||
|
|
||||||
func run() error {
|
func run() error {
|
||||||
var (
|
var (
|
||||||
configDir string
|
configDir string
|
||||||
cacheDir string
|
noColor *bool
|
||||||
pager string
|
|
||||||
imageViewer string
|
|
||||||
videoPlayer string
|
|
||||||
maxTerminalWidth int
|
|
||||||
noColor *bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.StringVar(&configDir, "config-dir", "", "Specify your config directory")
|
flag.StringVar(&configDir, "config-dir", "", "Specify your config directory")
|
||||||
flag.StringVar(&cacheDir, "cache-dir", "", "Specify your cache directory")
|
|
||||||
flag.StringVar(&pager, "pager", "", "Specify your preferred pager to page through long outputs. This is disabled by default.")
|
|
||||||
flag.StringVar(&imageViewer, "image-viewer", "", "Specify your favourite image viewer.")
|
|
||||||
flag.StringVar(&videoPlayer, "video-player", "", "Specify your favourite video player.")
|
|
||||||
flag.IntVar(&maxTerminalWidth, "max-terminal-width", 80, "Specify the maximum terminal width when displaying resources on screen.")
|
|
||||||
|
|
||||||
flag.BoolFunc("no-color", "Disable ANSI colour output when displaying text on screen", func(value string) error {
|
flag.BoolFunc("no-color", "Disable ANSI colour output when displaying text on screen", func(value string) error {
|
||||||
boolVal, err := strconv.ParseBool(value)
|
boolVal, err := strconv.ParseBool(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -67,7 +57,8 @@ func run() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If NoColor is still unspecified, check to see if the NO_COLOR environment variable is set
|
// If NoColor is still unspecified,
|
||||||
|
// check to see if the NO_COLOR environment variable is set
|
||||||
if noColor == nil {
|
if noColor == nil {
|
||||||
noColor = new(bool)
|
noColor = new(bool)
|
||||||
if os.Getenv("NO_COLOR") != "" {
|
if os.Getenv("NO_COLOR") != "" {
|
||||||
|
@ -80,110 +71,132 @@ func run() error {
|
||||||
command := flag.Arg(0)
|
command := flag.Arg(0)
|
||||||
args := flag.Args()[1:]
|
args := flag.Args()[1:]
|
||||||
|
|
||||||
printer := printer.NewPrinter(*noColor, pager, maxTerminalWidth)
|
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{
|
executorMap := map[string]executor.Executor{
|
||||||
executor.CommandAccept: executor.NewAcceptOrRejectExecutor(
|
executor.CommandAccept: executor.NewAcceptOrRejectExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandAccept,
|
executor.CommandAccept,
|
||||||
executor.CommandSummaryLookup(executor.CommandAccept),
|
executor.CommandSummaryLookup(executor.CommandAccept),
|
||||||
),
|
),
|
||||||
executor.CommandAdd: executor.NewAddExecutor(
|
executor.CommandAdd: executor.NewAddExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandAdd,
|
executor.CommandAdd,
|
||||||
executor.CommandSummaryLookup(executor.CommandAdd),
|
executor.CommandSummaryLookup(executor.CommandAdd),
|
||||||
),
|
),
|
||||||
executor.CommandBlock: executor.NewBlockOrUnblockExecutor(
|
executor.CommandBlock: executor.NewBlockOrUnblockExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandBlock,
|
executor.CommandBlock,
|
||||||
executor.CommandSummaryLookup(executor.CommandBlock),
|
executor.CommandSummaryLookup(executor.CommandBlock),
|
||||||
),
|
),
|
||||||
executor.CommandCreate: executor.NewCreateExecutor(
|
executor.CommandCreate: executor.NewCreateExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandCreate,
|
executor.CommandCreate,
|
||||||
executor.CommandSummaryLookup(executor.CommandCreate),
|
executor.CommandSummaryLookup(executor.CommandCreate),
|
||||||
),
|
),
|
||||||
executor.CommandDelete: executor.NewDeleteExecutor(
|
executor.CommandDelete: executor.NewDeleteExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandDelete,
|
executor.CommandDelete,
|
||||||
executor.CommandSummaryLookup(executor.CommandDelete),
|
executor.CommandSummaryLookup(executor.CommandDelete),
|
||||||
),
|
),
|
||||||
executor.CommandEdit: executor.NewEditExecutor(
|
executor.CommandEdit: executor.NewEditExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandEdit,
|
executor.CommandEdit,
|
||||||
executor.CommandSummaryLookup(executor.CommandEdit),
|
executor.CommandSummaryLookup(executor.CommandEdit),
|
||||||
),
|
),
|
||||||
executor.CommandFollow: executor.NewFollowOrUnfollowExecutor(
|
executor.CommandFollow: executor.NewFollowOrUnfollowExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandFollow,
|
executor.CommandFollow,
|
||||||
executor.CommandSummaryLookup(executor.CommandFollow),
|
executor.CommandSummaryLookup(executor.CommandFollow),
|
||||||
),
|
),
|
||||||
executor.CommandLogin: executor.NewLoginExecutor(
|
executor.CommandInit: executor.NewInitExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
configDir,
|
||||||
|
executor.CommandInit,
|
||||||
|
executor.CommandSummaryLookup(executor.CommandInit),
|
||||||
|
),
|
||||||
|
executor.CommandLogin: executor.NewLoginExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
executor.CommandLogin,
|
executor.CommandLogin,
|
||||||
executor.CommandSummaryLookup(executor.CommandLogin),
|
executor.CommandSummaryLookup(executor.CommandLogin),
|
||||||
),
|
),
|
||||||
executor.CommandMute: executor.NewMuteOrUnmuteExecutor(
|
executor.CommandMute: executor.NewMuteOrUnmuteExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandMute,
|
executor.CommandMute,
|
||||||
executor.CommandSummaryLookup(executor.CommandMute),
|
executor.CommandSummaryLookup(executor.CommandMute),
|
||||||
),
|
),
|
||||||
executor.CommandReject: executor.NewAcceptOrRejectExecutor(
|
executor.CommandReject: executor.NewAcceptOrRejectExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandReject,
|
executor.CommandReject,
|
||||||
executor.CommandSummaryLookup(executor.CommandReject),
|
executor.CommandSummaryLookup(executor.CommandReject),
|
||||||
),
|
),
|
||||||
executor.CommandRemove: executor.NewRemoveExecutor(
|
executor.CommandRemove: executor.NewRemoveExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandRemove,
|
executor.CommandRemove,
|
||||||
executor.CommandSummaryLookup(executor.CommandRemove),
|
executor.CommandSummaryLookup(executor.CommandRemove),
|
||||||
),
|
),
|
||||||
executor.CommandSwitch: executor.NewSwitchExecutor(
|
executor.CommandSwitch: executor.NewSwitchExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandSwitch,
|
executor.CommandSwitch,
|
||||||
executor.CommandSummaryLookup(executor.CommandSwitch),
|
executor.CommandSummaryLookup(executor.CommandSwitch),
|
||||||
),
|
),
|
||||||
executor.CommandUnfollow: executor.NewFollowOrUnfollowExecutor(
|
executor.CommandUnfollow: executor.NewFollowOrUnfollowExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandUnfollow,
|
executor.CommandUnfollow,
|
||||||
executor.CommandSummaryLookup(executor.CommandUnfollow),
|
executor.CommandSummaryLookup(executor.CommandUnfollow),
|
||||||
),
|
),
|
||||||
executor.CommandUnmute: executor.NewMuteOrUnmuteExecutor(
|
executor.CommandUnmute: executor.NewMuteOrUnmuteExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandUnmute,
|
executor.CommandUnmute,
|
||||||
executor.CommandSummaryLookup(executor.CommandUnmute),
|
executor.CommandSummaryLookup(executor.CommandUnmute),
|
||||||
),
|
),
|
||||||
executor.CommandUnblock: executor.NewBlockOrUnblockExecutor(
|
executor.CommandUnblock: executor.NewBlockOrUnblockExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandUnblock,
|
executor.CommandUnblock,
|
||||||
executor.CommandSummaryLookup(executor.CommandUnblock),
|
executor.CommandSummaryLookup(executor.CommandUnblock),
|
||||||
),
|
),
|
||||||
executor.CommandShow: executor.NewShowExecutor(
|
executor.CommandShow: executor.NewShowExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
cacheDir,
|
|
||||||
imageViewer,
|
|
||||||
videoPlayer,
|
|
||||||
executor.CommandShow,
|
executor.CommandShow,
|
||||||
executor.CommandSummaryLookup(executor.CommandShow),
|
executor.CommandSummaryLookup(executor.CommandShow),
|
||||||
),
|
),
|
||||||
executor.CommandVersion: executor.NewVersionExecutor(
|
executor.CommandVersion: executor.NewVersionExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
executor.CommandVersion,
|
executor.CommandVersion,
|
||||||
executor.CommandSummaryLookup(executor.CommandVersion),
|
executor.CommandSummaryLookup(executor.CommandVersion),
|
||||||
binaryVersion,
|
binaryVersion,
|
||||||
|
@ -192,8 +205,8 @@ func run() error {
|
||||||
gitCommit,
|
gitCommit,
|
||||||
),
|
),
|
||||||
executor.CommandWhoami: executor.NewWhoAmIExecutor(
|
executor.CommandWhoami: executor.NewWhoAmIExecutor(
|
||||||
printer,
|
enbasPrinter,
|
||||||
configDir,
|
enbasConfig,
|
||||||
executor.CommandWhoami,
|
executor.CommandWhoami,
|
||||||
executor.CommandSummaryLookup(executor.CommandWhoami),
|
executor.CommandSummaryLookup(executor.CommandWhoami),
|
||||||
),
|
),
|
||||||
|
@ -201,16 +214,16 @@ func run() error {
|
||||||
|
|
||||||
exe, ok := executorMap[command]
|
exe, ok := executorMap[command]
|
||||||
if !ok {
|
if !ok {
|
||||||
err := executor.UnknownCommandError{Command: command}
|
err = executor.UnknownCommandError{Command: command}
|
||||||
|
|
||||||
printer.PrintFailure(err.Error() + ".")
|
enbasPrinter.PrintFailure(err.Error() + ".")
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := executor.Execute(exe, args); err != nil {
|
if err = executor.Execute(exe, args); err != nil {
|
||||||
printer.PrintFailure("(" + command + ") " + err.Error() + ".")
|
enbasPrinter.PrintFailure("(" + command + ") " + err.Error() + ".")
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ type Client struct {
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientFromConfig(configDir string) (*Client, error) {
|
func NewClientFromFile(path string) (*Client, error) {
|
||||||
config, err := config.NewCredentialsConfigFromFile(configDir)
|
config, err := config.NewCredentialsConfigFromFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to get the authentication configuration: %w", err)
|
return nil, fmt.Errorf("unable to get the authentication configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func NewClient(authentication config.Credentials) *Client {
|
||||||
|
|
||||||
func (g *Client) AuthCodeURL() string {
|
func (g *Client) AuthCodeURL() string {
|
||||||
format := "%s/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code"
|
format := "%s/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code"
|
||||||
escapedRedirectURI := url.QueryEscape(internal.RedirectUri)
|
escapedRedirectURI := url.QueryEscape(internal.RedirectURI)
|
||||||
|
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
format,
|
format,
|
||||||
|
|
|
@ -24,7 +24,7 @@ type registerRequest struct {
|
||||||
func (g *Client) Register() error {
|
func (g *Client) Register() error {
|
||||||
params := registerRequest{
|
params := registerRequest{
|
||||||
ClientName: internal.ApplicationName,
|
ClientName: internal.ApplicationName,
|
||||||
RedirectUris: internal.RedirectUri,
|
RedirectUris: internal.RedirectURI,
|
||||||
Scopes: "read write",
|
Scopes: "read write",
|
||||||
Website: internal.ApplicationWebsite,
|
Website: internal.ApplicationWebsite,
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
var errEmptyAccessToken = errors.New("received an empty access token")
|
var errEmptyAccessToken = errors.New("received an empty access token")
|
||||||
|
|
||||||
type tokenRequest struct {
|
type tokenRequest struct {
|
||||||
RedirectUri string `json:"redirect_uri"`
|
RedirectURI string `json:"redirect_uri"`
|
||||||
ClientID string `json:"client_id"`
|
ClientID string `json:"client_id"`
|
||||||
ClientSecret string `json:"client_secret"`
|
ClientSecret string `json:"client_secret"`
|
||||||
GrantType string `json:"grant_type"`
|
GrantType string `json:"grant_type"`
|
||||||
|
@ -33,7 +33,7 @@ type tokenResponse struct {
|
||||||
|
|
||||||
func (g *Client) UpdateToken(code string) error {
|
func (g *Client) UpdateToken(code string) error {
|
||||||
params := tokenRequest{
|
params := tokenRequest{
|
||||||
RedirectUri: internal.RedirectUri,
|
RedirectURI: internal.RedirectURI,
|
||||||
ClientID: g.Authentication.ClientID,
|
ClientID: g.Authentication.ClientID,
|
||||||
ClientSecret: g.Authentication.ClientSecret,
|
ClientSecret: g.Authentication.ClientSecret,
|
||||||
GrantType: "authorization_code",
|
GrantType: "authorization_code",
|
||||||
|
|
108
internal/config/config.go
Normal file
108
internal/config/config.go
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
configFileName = "config.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
CredentialsFile string `json:"credentialsFile"`
|
||||||
|
CacheDirectory string `json:"cacheDirectory"`
|
||||||
|
LineWrapMaxWidth int `json:"lineWrapMaxWidth"`
|
||||||
|
HTTP HTTPConfig `json:"http"`
|
||||||
|
Integrations Integrations `json:"integrations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPConfig struct {
|
||||||
|
Timeout int `json:"timeout"`
|
||||||
|
MediaTimeout int `json:"mediaTimeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Integrations struct {
|
||||||
|
Browser string `json:"browser"`
|
||||||
|
Editor string `json:"editor"`
|
||||||
|
Pager string `json:"pager"`
|
||||||
|
ImageViewer string `json:"imageViewer"`
|
||||||
|
VideoPlayer string `json:"videoPlayer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigFromFile(configDir string) (*Config, error) {
|
||||||
|
path := configFile(configDir)
|
||||||
|
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to open %s: %w", path, err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
|
||||||
|
if err := json.NewDecoder(file).Decode(&config); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode the JSON data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FileExists(configDir string) (bool, error) {
|
||||||
|
path := configFile(configDir)
|
||||||
|
|
||||||
|
return utilities.FileExists(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveDefaultConfigToFile(configDir string) error {
|
||||||
|
path := configFile(configDir)
|
||||||
|
|
||||||
|
file, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the file at %s: %w", path, err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
config := defaultConfig(configDir)
|
||||||
|
encoder := json.NewEncoder(file)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
|
||||||
|
if err := encoder.Encode(config); err != nil {
|
||||||
|
return fmt.Errorf("unable to save the JSON data to the config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func configFile(configDir string) string {
|
||||||
|
return filepath.Join(utilities.CalculateConfigDir(configDir), configFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultConfig(configDir string) Config {
|
||||||
|
credentialsFilePath := defaultCredentialsConfigFile(configDir)
|
||||||
|
|
||||||
|
return Config{
|
||||||
|
CredentialsFile: credentialsFilePath,
|
||||||
|
CacheDirectory: "",
|
||||||
|
HTTP: HTTPConfig{
|
||||||
|
Timeout: 5,
|
||||||
|
MediaTimeout: 30,
|
||||||
|
},
|
||||||
|
LineWrapMaxWidth: 80,
|
||||||
|
Integrations: Integrations{
|
||||||
|
Browser: "",
|
||||||
|
Editor: "",
|
||||||
|
Pager: "",
|
||||||
|
ImageViewer: "",
|
||||||
|
VideoPlayer: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,13 +10,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
credentialsFileName = "credentials.json"
|
defaultCredentialsFileName = "credentials.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CredentialsConfig struct {
|
type CredentialsConfig struct {
|
||||||
|
@ -42,35 +41,29 @@ func (e CredentialsNotFoundError) Error() string {
|
||||||
// SaveCredentials saves the credentials into the credentials file within the specified configuration
|
// SaveCredentials saves the credentials into the credentials file within the specified configuration
|
||||||
// directory. If the directory is not specified then the default directory is used. If the directory
|
// directory. If the directory is not specified then the default directory is used. If the directory
|
||||||
// is not present, it will be created.
|
// is not present, it will be created.
|
||||||
func SaveCredentials(configDir, username string, credentials Credentials) (string, error) {
|
func SaveCredentials(filePath, username string, credentials Credentials) (string, error) {
|
||||||
if err := utilities.EnsureDirectory(utilities.CalculateConfigDir(configDir)); err != nil {
|
directory := filepath.Dir(filePath)
|
||||||
|
|
||||||
|
if err := utilities.EnsureDirectory(utilities.CalculateConfigDir(directory)); err != nil {
|
||||||
return "", fmt.Errorf("unable to ensure the configuration directory: %w", err)
|
return "", fmt.Errorf("unable to ensure the configuration directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var authConfig CredentialsConfig
|
var authConfig CredentialsConfig
|
||||||
|
|
||||||
filepath := credentialsConfigFile(configDir)
|
if _, err := os.Stat(filePath); err != nil {
|
||||||
|
|
||||||
if _, err := os.Stat(filepath); err != nil {
|
|
||||||
if !errors.Is(err, os.ErrNotExist) {
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
return "", fmt.Errorf("unknown error received when running stat on %s: %w", filepath, err)
|
return "", fmt.Errorf("unknown error received when running stat on %s: %w", filePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
authConfig.Credentials = make(map[string]Credentials)
|
authConfig.Credentials = make(map[string]Credentials)
|
||||||
} else {
|
} else {
|
||||||
authConfig, err = NewCredentialsConfigFromFile(configDir)
|
authConfig, err = NewCredentialsConfigFromFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err)
|
return "", fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instance := ""
|
instance := utilities.GetFQDN(credentials.Instance)
|
||||||
|
|
||||||
if strings.HasPrefix(credentials.Instance, "https://") {
|
|
||||||
instance = strings.TrimPrefix(credentials.Instance, "https://")
|
|
||||||
} else if strings.HasPrefix(credentials.Instance, "http://") {
|
|
||||||
instance = strings.TrimPrefix(credentials.Instance, "http://")
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticationName := username + "@" + instance
|
authenticationName := username + "@" + instance
|
||||||
|
|
||||||
|
@ -78,15 +71,15 @@ func SaveCredentials(configDir, username string, credentials Credentials) (strin
|
||||||
|
|
||||||
authConfig.Credentials[authenticationName] = credentials
|
authConfig.Credentials[authenticationName] = credentials
|
||||||
|
|
||||||
if err := saveCredentialsConfigFile(authConfig, configDir); err != nil {
|
if err := saveCredentialsConfigFile(authConfig, filePath); err != nil {
|
||||||
return "", fmt.Errorf("unable to save the authentication configuration to file: %w", err)
|
return "", fmt.Errorf("unable to save the authentication configuration to file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return authenticationName, nil
|
return authenticationName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateCurrentAccount(account string, configDir string) error {
|
func UpdateCurrentAccount(account string, filePath string) error {
|
||||||
credentialsConfig, err := NewCredentialsConfigFromFile(configDir)
|
credentialsConfig, err := NewCredentialsConfigFromFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err)
|
return fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -97,19 +90,19 @@ func UpdateCurrentAccount(account string, configDir string) error {
|
||||||
|
|
||||||
credentialsConfig.CurrentAccount = account
|
credentialsConfig.CurrentAccount = account
|
||||||
|
|
||||||
if err := saveCredentialsConfigFile(credentialsConfig, configDir); err != nil {
|
if err := saveCredentialsConfigFile(credentialsConfig, filePath); err != nil {
|
||||||
return fmt.Errorf("unable to save the authentication configuration to file: %w", err)
|
return fmt.Errorf("unable to save the authentication configuration to file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCredentialsConfigFromFile(configDir string) (CredentialsConfig, error) {
|
// NewCredentialsConfigFromFile creates a new CredentialsConfig value from reading
|
||||||
path := credentialsConfigFile(configDir)
|
// the credentials file.
|
||||||
|
func NewCredentialsConfigFromFile(filePath string) (CredentialsConfig, error) {
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CredentialsConfig{}, fmt.Errorf("unable to open %s, %w", path, err)
|
return CredentialsConfig{}, fmt.Errorf("unable to open %s: %w", filePath, err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
|
@ -122,14 +115,11 @@ func NewCredentialsConfigFromFile(configDir string) (CredentialsConfig, error) {
|
||||||
return authConfig, nil
|
return authConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveCredentialsConfigFile(authConfig CredentialsConfig, configDir string) error {
|
func saveCredentialsConfigFile(authConfig CredentialsConfig, filePath string) error {
|
||||||
path := credentialsConfigFile(configDir)
|
file, err := os.Create(filePath)
|
||||||
|
|
||||||
file, err := os.Create(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to open %s: %w", path, err)
|
return fmt.Errorf("unable to create the file at %s: %w", filePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
encoder := json.NewEncoder(file)
|
encoder := json.NewEncoder(file)
|
||||||
|
@ -142,6 +132,6 @@ func saveCredentialsConfigFile(authConfig CredentialsConfig, configDir string) e
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func credentialsConfigFile(configDir string) string {
|
func defaultCredentialsConfigFile(configDir string) string {
|
||||||
return filepath.Join(utilities.CalculateConfigDir(configDir), credentialsFileName)
|
return filepath.Join(utilities.CalculateConfigDir(configDir), defaultCredentialsFileName)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,19 +17,19 @@ type AcceptOrRejectExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
configDir string
|
config *config.Config
|
||||||
resourceType string
|
resourceType string
|
||||||
accountName string
|
accountName string
|
||||||
command string
|
command string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAcceptOrRejectExecutor(enbasPrinter *printer.Printer, configDir, name, summary string) *AcceptOrRejectExecutor {
|
func NewAcceptOrRejectExecutor(enbasPrinter *printer.Printer, config *config.Config, name, summary string) *AcceptOrRejectExecutor {
|
||||||
acceptExe := AcceptOrRejectExecutor{
|
acceptExe := AcceptOrRejectExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: enbasPrinter,
|
printer: enbasPrinter,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
command: name,
|
command: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptExe.StringVar(&acceptExe.resourceType, flagType, "", "Specify the type of resource to accept or reject")
|
acceptExe.StringVar(&acceptExe.resourceType, flagType, "", "Specify the type of resource to accept or reject")
|
||||||
|
@ -49,7 +50,7 @@ func (a *AcceptOrRejectExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: a.resourceType}
|
return UnsupportedTypeError{resourceType: a.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(a.configDir)
|
gtsClient, err := client.NewClientFromFile(a.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -58,7 +59,7 @@ func (a *AcceptOrRejectExecutor) Execute() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AcceptOrRejectExecutor) acceptOrRejectFollowRequest(gtsClient *client.Client) error {
|
func (a *AcceptOrRejectExecutor) acceptOrRejectFollowRequest(gtsClient *client.Client) error {
|
||||||
accountID, err := getAccountID(gtsClient, false, a.accountName, a.configDir)
|
accountID, err := getAccountID(gtsClient, false, a.accountName, a.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getAccountID(gtsClient *client.Client, myAccount bool, accountName, configDir string) (string, error) {
|
func getAccountID(gtsClient *client.Client, myAccount bool, accountName, path string) (string, error) {
|
||||||
var (
|
var (
|
||||||
accountID string
|
accountID string
|
||||||
err error
|
err error
|
||||||
|
@ -20,7 +20,7 @@ func getAccountID(gtsClient *client.Client, myAccount bool, accountName, configD
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case myAccount:
|
case myAccount:
|
||||||
accountID, err = getMyAccountID(gtsClient, configDir)
|
accountID, err = getMyAccountID(gtsClient, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to get your account ID: %w", err)
|
return "", fmt.Errorf("unable to get your account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -45,8 +45,8 @@ func getTheirAccountID(gtsClient *client.Client, accountURI string) (string, err
|
||||||
return account.ID, nil
|
return account.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMyAccountID(gtsClient *client.Client, configDir string) (string, error) {
|
func getMyAccountID(gtsClient *client.Client, path string) (string, error) {
|
||||||
account, err := getMyAccount(gtsClient, configDir)
|
account, err := getMyAccount(gtsClient, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("received an error while getting your account details: %w", err)
|
return "", fmt.Errorf("received an error while getting your account details: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,8 @@ func getMyAccountID(gtsClient *client.Client, configDir string) (string, error)
|
||||||
return account.ID, nil
|
return account.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMyAccount(gtsClient *client.Client, configDir string) (model.Account, error) {
|
func getMyAccount(gtsClient *client.Client, path string) (model.Account, error) {
|
||||||
authConfig, err := config.NewCredentialsConfigFromFile(configDir)
|
authConfig, err := config.NewCredentialsConfigFromFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Account{}, fmt.Errorf("unable to retrieve the authentication configuration: %w", err)
|
return model.Account{}, fmt.Errorf("unable to retrieve the authentication configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ type AddExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
configDir string
|
config *config.Config
|
||||||
resourceType string
|
resourceType string
|
||||||
toResourceType string
|
toResourceType string
|
||||||
listID string
|
listID string
|
||||||
|
@ -28,14 +29,14 @@ type AddExecutor struct {
|
||||||
content string
|
content string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAddExecutor(printer *printer.Printer, configDir, name, summary string) *AddExecutor {
|
func NewAddExecutor(printer *printer.Printer, config *config.Config, name, summary string) *AddExecutor {
|
||||||
emptyArr := make([]string, 0, 3)
|
emptyArr := make([]string, 0, 3)
|
||||||
|
|
||||||
addExe := AddExecutor{
|
addExe := AddExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
accountNames: MultiStringFlagValue(emptyArr),
|
accountNames: MultiStringFlagValue(emptyArr),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ func (a *AddExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: a.toResourceType}
|
return UnsupportedTypeError{resourceType: a.toResourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(a.configDir)
|
gtsClient, err := client.NewClientFromFile(a.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +155,7 @@ func (a *AddExecutor) addNoteToAccount(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unexpected number of accounts specified: want 1, got %d", len(a.accountNames))
|
return fmt.Errorf("unexpected number of accounts specified: want 1, got %d", len(a.accountNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
accountID, err := getAccountID(gtsClient, false, a.accountNames[0], a.configDir)
|
accountID, err := getAccountID(gtsClient, false, a.accountNames[0], a.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,19 +17,19 @@ type BlockOrUnblockExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
configDir string
|
config *config.Config
|
||||||
resourceType string
|
resourceType string
|
||||||
accountName string
|
accountName string
|
||||||
command string
|
command string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockOrUnblockExecutor(printer *printer.Printer, configDir, name, summary string) *BlockOrUnblockExecutor {
|
func NewBlockOrUnblockExecutor(printer *printer.Printer, config *config.Config, name, summary string) *BlockOrUnblockExecutor {
|
||||||
blockExe := BlockOrUnblockExecutor{
|
blockExe := BlockOrUnblockExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
command: name,
|
command: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
blockExe.StringVar(&blockExe.resourceType, flagType, "", "Specify the type of resource to block or unblock")
|
blockExe.StringVar(&blockExe.resourceType, flagType, "", "Specify the type of resource to block or unblock")
|
||||||
|
@ -49,7 +50,7 @@ func (b *BlockOrUnblockExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: b.resourceType}
|
return UnsupportedTypeError{resourceType: b.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(b.configDir)
|
gtsClient, err := client.NewClientFromFile(b.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -62,7 +63,7 @@ func (b *BlockOrUnblockExecutor) blockOrUnblockAccount(gtsClient *client.Client)
|
||||||
return FlagNotSetError{flagText: flagAccountName}
|
return FlagNotSetError{flagText: flagAccountName}
|
||||||
}
|
}
|
||||||
|
|
||||||
accountID, err := getAccountID(gtsClient, false, b.accountName, b.configDir)
|
accountID, err := getAccountID(gtsClient, false, b.accountName, b.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ const (
|
||||||
CommandDelete string = "delete"
|
CommandDelete string = "delete"
|
||||||
CommandEdit string = "edit"
|
CommandEdit string = "edit"
|
||||||
CommandFollow string = "follow"
|
CommandFollow string = "follow"
|
||||||
|
CommandInit string = "init"
|
||||||
CommandLogin string = "login"
|
CommandLogin string = "login"
|
||||||
CommandMute string = "mute"
|
CommandMute string = "mute"
|
||||||
CommandReject string = "reject"
|
CommandReject string = "reject"
|
||||||
|
@ -31,6 +32,7 @@ const (
|
||||||
commandDeleteSummary string = "Delete a specific resource"
|
commandDeleteSummary string = "Delete a specific resource"
|
||||||
commandEditSummary string = "Edit a specific resource"
|
commandEditSummary string = "Edit a specific resource"
|
||||||
commandFollowSummary string = "Follow a resource (e.g. an account)"
|
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"
|
commandLoginSummary string = "Login to an account on GoToSocial"
|
||||||
commandMuteSummary string = "Mute a resource (e.g. an account)"
|
commandMuteSummary string = "Mute a resource (e.g. an account)"
|
||||||
commandRejectSummary string = "Reject a request (e.g. a follow request)"
|
commandRejectSummary string = "Reject a request (e.g. a follow request)"
|
||||||
|
@ -53,6 +55,7 @@ func CommandSummaryMap() map[string]string {
|
||||||
CommandDelete: commandDeleteSummary,
|
CommandDelete: commandDeleteSummary,
|
||||||
CommandEdit: commandEditSummary,
|
CommandEdit: commandEditSummary,
|
||||||
CommandFollow: commandFollowSummary,
|
CommandFollow: commandFollowSummary,
|
||||||
|
CommandInit: commandInitSummary,
|
||||||
CommandLogin: commandLoginSummary,
|
CommandLogin: commandLoginSummary,
|
||||||
CommandMute: commandMuteSummary,
|
CommandMute: commandMuteSummary,
|
||||||
CommandReject: commandRejectSummary,
|
CommandReject: commandRejectSummary,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
|
@ -19,6 +20,7 @@ type CreateExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
addPoll bool
|
addPoll bool
|
||||||
boostable bool
|
boostable bool
|
||||||
federated bool
|
federated bool
|
||||||
|
@ -27,7 +29,6 @@ type CreateExecutor struct {
|
||||||
pollHidesVoteCounts bool
|
pollHidesVoteCounts bool
|
||||||
replyable bool
|
replyable bool
|
||||||
sensitive *bool
|
sensitive *bool
|
||||||
configDir string
|
|
||||||
content string
|
content string
|
||||||
contentType string
|
contentType string
|
||||||
fromFile string
|
fromFile string
|
||||||
|
@ -41,12 +42,12 @@ type CreateExecutor struct {
|
||||||
pollOptions MultiStringFlagValue
|
pollOptions MultiStringFlagValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCreateExecutor(printer *printer.Printer, configDir, name, summary string) *CreateExecutor {
|
func NewCreateExecutor(printer *printer.Printer, config *config.Config, name, summary string) *CreateExecutor {
|
||||||
createExe := CreateExecutor{
|
createExe := CreateExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
createExe.BoolVar(&createExe.boostable, flagEnableReposts, true, "Specify if the status can be reposted/boosted by others")
|
createExe.BoolVar(&createExe.boostable, flagEnableReposts, true, "Specify if the status can be reposted/boosted by others")
|
||||||
|
@ -90,7 +91,7 @@ func (c *CreateExecutor) Execute() error {
|
||||||
return FlagNotSetError{flagText: flagType}
|
return FlagNotSetError{flagText: flagType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(c.configDir)
|
gtsClient, err := client.NewClientFromFile(c.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,17 +17,17 @@ type DeleteExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
configDir string
|
config *config.Config
|
||||||
resourceType string
|
resourceType string
|
||||||
listID string
|
listID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDeleteExecutor(printer *printer.Printer, configDir, name, summary string) *DeleteExecutor {
|
func NewDeleteExecutor(printer *printer.Printer, config *config.Config, name, summary string) *DeleteExecutor {
|
||||||
deleteExe := DeleteExecutor{
|
deleteExe := DeleteExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteExe.StringVar(&deleteExe.resourceType, flagType, "", "Specify the type of resource to delete")
|
deleteExe.StringVar(&deleteExe.resourceType, flagType, "", "Specify the type of resource to delete")
|
||||||
|
@ -51,7 +52,7 @@ func (d *DeleteExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: d.resourceType}
|
return UnsupportedTypeError{resourceType: d.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(d.configDir)
|
gtsClient, err := client.NewClientFromFile(d.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
@ -17,19 +18,19 @@ type EditExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
configDir string
|
config *config.Config
|
||||||
resourceType string
|
resourceType string
|
||||||
listID string
|
listID string
|
||||||
listTitle string
|
listTitle string
|
||||||
listRepliesPolicy string
|
listRepliesPolicy string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEditExecutor(printer *printer.Printer, configDir, name, summary string) *EditExecutor {
|
func NewEditExecutor(printer *printer.Printer, config *config.Config, name, summary string) *EditExecutor {
|
||||||
editExe := EditExecutor{
|
editExe := EditExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
editExe.StringVar(&editExe.resourceType, flagType, "", "Specify the type of resource to update")
|
editExe.StringVar(&editExe.resourceType, flagType, "", "Specify the type of resource to update")
|
||||||
|
@ -56,7 +57,7 @@ func (e *EditExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: e.resourceType}
|
return UnsupportedTypeError{resourceType: e.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(e.configDir)
|
gtsClient, err := client.NewClientFromFile(e.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,3 +102,11 @@ type NotFollowingError struct {
|
||||||
func (e NotFollowingError) Error() string {
|
func (e NotFollowingError) Error() string {
|
||||||
return "you are not following " + e.Account
|
return "you are not following " + e.Account
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UnknownMediaAttachmentError struct {
|
||||||
|
AttachmentID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e UnknownMediaAttachmentError) Error() string {
|
||||||
|
return "unknown media attachment '" + e.AttachmentID + "'"
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ type FollowOrUnfollowExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
configDir string
|
config *config.Config
|
||||||
resourceType string
|
resourceType string
|
||||||
accountName string
|
accountName string
|
||||||
showReposts bool
|
showReposts bool
|
||||||
|
@ -24,13 +25,13 @@ type FollowOrUnfollowExecutor struct {
|
||||||
action string
|
action string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFollowOrUnfollowExecutor(printer *printer.Printer, configDir, name, summary string) *FollowOrUnfollowExecutor {
|
func NewFollowOrUnfollowExecutor(printer *printer.Printer, config *config.Config, name, summary string) *FollowOrUnfollowExecutor {
|
||||||
command := FollowOrUnfollowExecutor{
|
command := FollowOrUnfollowExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
action: name,
|
action: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
command.StringVar(&command.resourceType, flagType, "", "Specify the type of resource to follow")
|
command.StringVar(&command.resourceType, flagType, "", "Specify the type of resource to follow")
|
||||||
|
@ -53,7 +54,7 @@ func (f *FollowOrUnfollowExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: f.resourceType}
|
return UnsupportedTypeError{resourceType: f.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(f.configDir)
|
gtsClient, err := client.NewClientFromFile(f.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -62,7 +63,7 @@ func (f *FollowOrUnfollowExecutor) Execute() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FollowOrUnfollowExecutor) followOrUnfollowAccount(gtsClient *client.Client) error {
|
func (f *FollowOrUnfollowExecutor) followOrUnfollowAccount(gtsClient *client.Client) error {
|
||||||
accountID, err := getAccountID(gtsClient, false, f.accountName, f.configDir)
|
accountID, err := getAccountID(gtsClient, false, f.accountName, f.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
61
internal/executor/init.go
Normal file
61
internal/executor/init.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
i.printer.PrintSuccess("The configuration directory is present.")
|
||||||
|
|
||||||
|
fileExists, err := config.FileExists(i.configDir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to check if the config file exists: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileExists {
|
||||||
|
i.printer.PrintInfo("The configuration file is already present in " + i.configDir + "\n")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := config.SaveDefaultConfigToFile(i.configDir); err != nil {
|
||||||
|
return fmt.Errorf("unable to create a new configuration file in %s: %w", i.configDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
i.printer.PrintSuccess("Successfully created a new configuration file in " + i.configDir)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -18,18 +18,18 @@ import (
|
||||||
type LoginExecutor struct {
|
type LoginExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
configDir string
|
config *config.Config
|
||||||
instance string
|
instance string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLoginExecutor(printer *printer.Printer, configDir, name, summary string) *LoginExecutor {
|
func NewLoginExecutor(printer *printer.Printer, config *config.Config, name, summary string) *LoginExecutor {
|
||||||
command := LoginExecutor{
|
command := LoginExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
instance: "",
|
instance: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
command.StringVar(&command.instance, flagInstance, "", "Specify the instance that you want to login to.")
|
command.StringVar(&command.instance, flagInstance, "", "Specify the instance that you want to login to.")
|
||||||
|
@ -95,7 +95,7 @@ func (c *LoginExecutor) Execute() error {
|
||||||
return fmt.Errorf("unable to verify the credentials: %w", err)
|
return fmt.Errorf("unable to verify the credentials: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
loginName, err := config.SaveCredentials(c.configDir, account.Username, gtsClient.Authentication)
|
loginName, err := config.SaveCredentials(c.config.CredentialsFile, account.Username, gtsClient.Authentication)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to save the authentication details: %w", err)
|
return fmt.Errorf("unable to save the authentication details: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,21 +17,21 @@ type MuteOrUnmuteExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
accountName string
|
accountName string
|
||||||
configDir string
|
|
||||||
command string
|
command string
|
||||||
resourceType string
|
resourceType string
|
||||||
muteDuration TimeDurationFlagValue
|
muteDuration TimeDurationFlagValue
|
||||||
muteNotifications bool
|
muteNotifications bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMuteOrUnmuteExecutor(printer *printer.Printer, configDir, name, summary string) *MuteOrUnmuteExecutor {
|
func NewMuteOrUnmuteExecutor(printer *printer.Printer, config *config.Config, name, summary string) *MuteOrUnmuteExecutor {
|
||||||
exe := MuteOrUnmuteExecutor{
|
exe := MuteOrUnmuteExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
command: name,
|
command: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
exe.StringVar(&exe.accountName, flagAccountName, "", "Specify the account name in full (username@domain)")
|
exe.StringVar(&exe.accountName, flagAccountName, "", "Specify the account name in full (username@domain)")
|
||||||
|
@ -53,7 +54,7 @@ func (m *MuteOrUnmuteExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: m.resourceType}
|
return UnsupportedTypeError{resourceType: m.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(m.configDir)
|
gtsClient, err := client.NewClientFromFile(m.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +67,7 @@ func (m *MuteOrUnmuteExecutor) muteOrUnmuteAccount(gtsClient *client.Client) err
|
||||||
return FlagNotSetError{flagText: flagAccountName}
|
return FlagNotSetError{flagText: flagAccountName}
|
||||||
}
|
}
|
||||||
|
|
||||||
accountID, err := getAccountID(gtsClient, false, m.accountName, m.configDir)
|
accountID, err := getAccountID(gtsClient, false, m.accountName, m.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ type RemoveExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
configDir string
|
config *config.Config
|
||||||
resourceType string
|
resourceType string
|
||||||
fromResourceType string
|
fromResourceType string
|
||||||
listID string
|
listID string
|
||||||
|
@ -24,14 +25,14 @@ type RemoveExecutor struct {
|
||||||
accountNames MultiStringFlagValue
|
accountNames MultiStringFlagValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRemoveExecutor(printer *printer.Printer, configDir, name, summary string) *RemoveExecutor {
|
func NewRemoveExecutor(printer *printer.Printer, config *config.Config, name, summary string) *RemoveExecutor {
|
||||||
emptyArr := make([]string, 0, 3)
|
emptyArr := make([]string, 0, 3)
|
||||||
|
|
||||||
removeExe := RemoveExecutor{
|
removeExe := RemoveExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
accountNames: MultiStringFlagValue(emptyArr),
|
accountNames: MultiStringFlagValue(emptyArr),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ func (r *RemoveExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: r.fromResourceType}
|
return UnsupportedTypeError{resourceType: r.fromResourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(r.configDir)
|
gtsClient, err := client.NewClientFromFile(r.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -137,7 +138,7 @@ func (r *RemoveExecutor) removeNoteFromAccount(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unexpected number of accounts specified: want 1, got %d", len(r.accountNames))
|
return fmt.Errorf("unexpected number of accounts specified: want 1, got %d", len(r.accountNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
accountID, err := getAccountID(gtsClient, false, r.accountNames[0], r.configDir)
|
accountID, err := getAccountID(gtsClient, false, r.accountNames[0], r.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
|
@ -20,12 +21,11 @@ type ShowExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
myAccount bool
|
myAccount bool
|
||||||
skipAccountRelationship bool
|
skipAccountRelationship bool
|
||||||
showUserPreferences bool
|
showUserPreferences bool
|
||||||
showInBrowser bool
|
showInBrowser bool
|
||||||
configDir string
|
|
||||||
cacheRoot string
|
|
||||||
resourceType string
|
resourceType string
|
||||||
accountName string
|
accountName string
|
||||||
statusID string
|
statusID string
|
||||||
|
@ -34,21 +34,16 @@ type ShowExecutor struct {
|
||||||
tag string
|
tag string
|
||||||
pollID string
|
pollID string
|
||||||
fromResourceType string
|
fromResourceType string
|
||||||
imageViewer string
|
|
||||||
videoPlayer string
|
|
||||||
limit int
|
limit int
|
||||||
attachmentIDs MultiStringFlagValue
|
attachmentIDs MultiStringFlagValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShowExecutor(printer *printer.Printer, configDir, cacheRoot, imageViewer, videoPlayer, name, summary string) *ShowExecutor {
|
func NewShowExecutor(printer *printer.Printer, config *config.Config, name, summary string) *ShowExecutor {
|
||||||
showExe := ShowExecutor{
|
showExe := ShowExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
cacheRoot: cacheRoot,
|
|
||||||
imageViewer: imageViewer,
|
|
||||||
videoPlayer: videoPlayer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showExe.BoolVar(&showExe.myAccount, flagMyAccount, false, "Set to true to lookup your account")
|
showExe.BoolVar(&showExe.myAccount, flagMyAccount, false, "Set to true to lookup your account")
|
||||||
|
@ -100,7 +95,7 @@ func (s *ShowExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: s.resourceType}
|
return UnsupportedTypeError{resourceType: s.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(s.configDir)
|
gtsClient, err := client.NewClientFromFile(s.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -126,7 +121,7 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
||||||
)
|
)
|
||||||
|
|
||||||
if s.myAccount {
|
if s.myAccount {
|
||||||
account, err = getMyAccount(gtsClient, s.configDir)
|
account, err = getMyAccount(gtsClient, s.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account details: %w", err)
|
return fmt.Errorf("received an error while getting the account details: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -288,7 +283,7 @@ func (s *ShowExecutor) showLists(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShowExecutor) showFollowers(gtsClient *client.Client) error {
|
func (s *ShowExecutor) showFollowers(gtsClient *client.Client) error {
|
||||||
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.configDir)
|
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -308,7 +303,7 @@ func (s *ShowExecutor) showFollowers(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShowExecutor) showFollowing(gtsClient *client.Client) error {
|
func (s *ShowExecutor) showFollowing(gtsClient *client.Client) error {
|
||||||
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.configDir)
|
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -471,7 +466,7 @@ func (s *ShowExecutor) showMediaFromStatus(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheDir := filepath.Join(
|
cacheDir := filepath.Join(
|
||||||
utilities.CalculateCacheDir(s.cacheRoot, utilities.GetFQDN(gtsClient.Authentication.Instance)),
|
utilities.CalculateCacheDir(s.config.CacheDirectory, utilities.GetFQDN(gtsClient.Authentication.Instance)),
|
||||||
"media",
|
"media",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -498,7 +493,7 @@ func (s *ShowExecutor) showMediaFromStatus(gtsClient *client.Client) error {
|
||||||
for _, attachmentID := range s.attachmentIDs {
|
for _, attachmentID := range s.attachmentIDs {
|
||||||
mediaObj, ok := attachmentsHashMap[attachmentID]
|
mediaObj, ok := attachmentsHashMap[attachmentID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unknown media attachment: %s", attachmentID)
|
return UnknownMediaAttachmentError{AttachmentID: attachmentID}
|
||||||
}
|
}
|
||||||
|
|
||||||
split := strings.Split(mediaObj.url, "/")
|
split := strings.Split(mediaObj.url, "/")
|
||||||
|
@ -533,13 +528,13 @@ func (s *ShowExecutor) showMediaFromStatus(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(imageFiles) > 0 {
|
if len(imageFiles) > 0 {
|
||||||
if err := utilities.OpenMedia(s.imageViewer, imageFiles); err != nil {
|
if err := utilities.OpenMedia(s.config.Integrations.ImageViewer, imageFiles); err != nil {
|
||||||
return fmt.Errorf("unable to open the image viewer: %w", err)
|
return fmt.Errorf("unable to open the image viewer: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(videoFiles) > 0 {
|
if len(videoFiles) > 0 {
|
||||||
if err := utilities.OpenMedia(s.videoPlayer, videoFiles); err != nil {
|
if err := utilities.OpenMedia(s.config.Integrations.VideoPlayer, videoFiles); err != nil {
|
||||||
return fmt.Errorf("unable to open the video player: %w", err)
|
return fmt.Errorf("unable to open the video player: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,17 +15,17 @@ import (
|
||||||
type SwitchExecutor struct {
|
type SwitchExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
configDir string
|
config *config.Config
|
||||||
toResourceType string
|
toResourceType string
|
||||||
accountName string
|
accountName string
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSwitchExecutor(printer *printer.Printer, configDir, name, summary string) *SwitchExecutor {
|
func NewSwitchExecutor(printer *printer.Printer, config *config.Config, name, summary string) *SwitchExecutor {
|
||||||
switchExe := SwitchExecutor{
|
switchExe := SwitchExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
switchExe.StringVar(&switchExe.toResourceType, flagTo, "", "The account to switch to")
|
switchExe.StringVar(&switchExe.toResourceType, flagTo, "", "The account to switch to")
|
||||||
|
@ -54,7 +54,7 @@ func (s *SwitchExecutor) switchToAccount() error {
|
||||||
return NoAccountSpecifiedError{}
|
return NoAccountSpecifiedError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.UpdateCurrentAccount(s.accountName, s.configDir); 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)
|
return fmt.Errorf("unable to switch account to the account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,27 +15,41 @@ func commandUsageFunc(name, summary string, flagset *flag.FlagSet) func() {
|
||||||
return func() {
|
return func() {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
|
|
||||||
fmt.Fprintf(
|
builder.WriteString("SUMMARY:")
|
||||||
&builder,
|
builder.WriteString("\n " + name + " - " + summary)
|
||||||
"SUMMARY:\n %s - %s\n\nUSAGE:\n enbas %s [flags]\n\nFLAGS:",
|
builder.WriteString("\n\nUSAGE:")
|
||||||
name,
|
builder.WriteString("\n enbas " + name)
|
||||||
summary,
|
|
||||||
name,
|
flagMap := make(map[string]string)
|
||||||
)
|
|
||||||
|
|
||||||
flagset.VisitAll(func(f *flag.Flag) {
|
flagset.VisitAll(func(f *flag.Flag) {
|
||||||
fmt.Fprintf(
|
flagMap[f.Name] = f.Usage
|
||||||
&builder,
|
|
||||||
"\n --%s\n %s",
|
|
||||||
f.Name,
|
|
||||||
f.Usage,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if len(flagMap) > 0 {
|
||||||
|
flags := make([]string, len(flagMap))
|
||||||
|
ind := 0
|
||||||
|
|
||||||
|
for f := range flagMap {
|
||||||
|
flags[ind] = f
|
||||||
|
ind++
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.Sort(flags)
|
||||||
|
|
||||||
|
builder.WriteString(" [flags]")
|
||||||
|
builder.WriteString("\n\nFLAGS:")
|
||||||
|
|
||||||
|
for _, value := range flags {
|
||||||
|
builder.WriteString("\n --" + value)
|
||||||
|
builder.WriteString("\n " + flagMap[value])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
builder.WriteString("\n")
|
builder.WriteString("\n")
|
||||||
|
|
||||||
w := flag.CommandLine.Output()
|
w := flag.CommandLine.Output()
|
||||||
|
|
||||||
fmt.Fprint(w, builder.String())
|
_, _ = w.Write([]byte(builder.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
type VersionExecutor struct {
|
type VersionExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
showFullVersion bool
|
showFullVersion bool
|
||||||
binaryVersion string
|
binaryVersion string
|
||||||
|
@ -21,7 +22,7 @@ type VersionExecutor struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVersionExecutor(
|
func NewVersionExecutor(
|
||||||
enbasPrinter *printer.Printer,
|
printer *printer.Printer,
|
||||||
name,
|
name,
|
||||||
summary,
|
summary,
|
||||||
binaryVersion,
|
binaryVersion,
|
||||||
|
@ -32,7 +33,7 @@ func NewVersionExecutor(
|
||||||
command := VersionExecutor{
|
command := VersionExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: enbasPrinter,
|
printer: printer,
|
||||||
binaryVersion: binaryVersion,
|
binaryVersion: binaryVersion,
|
||||||
buildTime: buildTime,
|
buildTime: buildTime,
|
||||||
goVersion: goVersion,
|
goVersion: goVersion,
|
||||||
|
|
|
@ -15,16 +15,16 @@ import (
|
||||||
type WhoAmIExecutor struct {
|
type WhoAmIExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
configDir string
|
config *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWhoAmIExecutor(printer *printer.Printer, configDir, name, summary string) *WhoAmIExecutor {
|
func NewWhoAmIExecutor(printer *printer.Printer, config *config.Config, name, summary string) *WhoAmIExecutor {
|
||||||
whoExe := WhoAmIExecutor{
|
whoExe := WhoAmIExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
printer: printer,
|
printer: printer,
|
||||||
configDir: configDir,
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
whoExe.Usage = commandUsageFunc(name, summary, whoExe.FlagSet)
|
whoExe.Usage = commandUsageFunc(name, summary, whoExe.FlagSet)
|
||||||
|
@ -33,7 +33,7 @@ func NewWhoAmIExecutor(printer *printer.Printer, configDir, name, summary string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *WhoAmIExecutor) Execute() error {
|
func (c *WhoAmIExecutor) Execute() error {
|
||||||
config, err := config.NewCredentialsConfigFromFile(c.configDir)
|
config, err := config.NewCredentialsConfigFromFile(c.config.CredentialsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to load the credential config: %w", err)
|
return fmt.Errorf("unable to load the credential config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,6 @@ package internal
|
||||||
const (
|
const (
|
||||||
ApplicationName = "enbas"
|
ApplicationName = "enbas"
|
||||||
ApplicationWebsite = "https://codeflow.dananglin.me.uk/apollo/enbas"
|
ApplicationWebsite = "https://codeflow.dananglin.me.uk/apollo/enbas"
|
||||||
RedirectUri = "urn:ietf:wg:oauth:2.0:oob"
|
RedirectURI = "urn:ietf:wg:oauth:2.0:oob"
|
||||||
UserAgent = "Enbas/0.0.0"
|
UserAgent = "Enbas/0.0.0"
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,7 @@ type Application struct {
|
||||||
ClientSecret string `json:"client_secret"`
|
ClientSecret string `json:"client_secret"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
RedirectUri string `json:"redirect_uri"`
|
RedirectURI string `json:"redirect_uri"`
|
||||||
VapidKey string `json:"vapid_key"`
|
VapidKey string `json:"vapid_key"`
|
||||||
Website string `json:"website"`
|
Website string `json:"website"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ type InstanceV2Thumbnail struct {
|
||||||
ThumbnailDescription string `json:"thumbnail_description"`
|
ThumbnailDescription string `json:"thumbnail_description"`
|
||||||
ThumbnailType string `json:"thumbnail_type"`
|
ThumbnailType string `json:"thumbnail_type"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Versions InstanceV2ThumbnailVersions `json:"versions"`
|
Versions InstanceV2ThumbnailVersions `json:"versions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstanceV2ThumbnailVersions struct {
|
type InstanceV2ThumbnailVersions struct {
|
||||||
|
|
|
@ -20,7 +20,7 @@ func CalculateConfigDir(configDir string) string {
|
||||||
|
|
||||||
configRoot, err := os.UserConfigDir()
|
configRoot, err := os.UserConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
configRoot = "."
|
return filepath.Join(os.Getenv("HOME"), "."+internal.ApplicationName, "config")
|
||||||
}
|
}
|
||||||
|
|
||||||
return filepath.Join(configRoot, internal.ApplicationName)
|
return filepath.Join(configRoot, internal.ApplicationName)
|
||||||
|
@ -33,7 +33,7 @@ func CalculateCacheDir(cacheDir, instanceFQDN string) string {
|
||||||
|
|
||||||
cacheRoot, err := os.UserCacheDir()
|
cacheRoot, err := os.UserCacheDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cacheRoot = "."
|
return filepath.Join(os.Getenv("HOME"), "."+internal.ApplicationName, "cache")
|
||||||
}
|
}
|
||||||
|
|
||||||
return filepath.Join(cacheRoot, internal.ApplicationName, instanceFQDN)
|
return filepath.Join(cacheRoot, internal.ApplicationName, instanceFQDN)
|
||||||
|
|
Loading…
Reference in a new issue