fix: add a new internal printer
Add a new internal printer package for printing resources to the screen or pager. With the new printer in place, most of the settings such as the pager command, colour theme, whether or not colour output is disabled, etc are defined in one place which saves us the trouble of passing an increasing number of parameters to an increasing number of Display methods throughout the code base. The old Displayer interface and associated Display methods in the model package are removed as this is now handled by the printer. The format functions in the utilities package has essentially been rewritten as methods to the Printer type. Additional changes: - All indentation when displaying information about resources (e.g. statuses, instance, accounts) are removed. - The application's build information now has colour output.
This commit is contained in:
parent
f73f1f5872
commit
ccdd8b6530
33 changed files with 938 additions and 808 deletions
|
@ -11,6 +11,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -22,47 +23,33 @@ var (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := run(); err != nil {
|
if err := run(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: %v.\n", err)
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func run() error {
|
func run() error {
|
||||||
topLevelFlags := executor.TopLevelFlags{
|
var (
|
||||||
ConfigDir: "",
|
configDir string
|
||||||
NoColor: nil,
|
pager string
|
||||||
Pager: "",
|
maxTerminalWidth int
|
||||||
}
|
noColor *bool
|
||||||
|
|
||||||
flag.StringVar(
|
|
||||||
&topLevelFlags.ConfigDir,
|
|
||||||
"config-dir",
|
|
||||||
"",
|
|
||||||
"Specify your config directory",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.BoolFunc(
|
flag.StringVar(&configDir, "config-dir", "", "Specify your config directory")
|
||||||
"no-color",
|
flag.StringVar(&pager, "pager", "", "Specify your preferred pager to page through long outputs. This is disabled by default.")
|
||||||
"Disable ANSI colour output when displaying text on screen",
|
flag.IntVar(&maxTerminalWidth, "max-terminal-width", 80, "Specify the maximum terminal width when displaying resources 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 {
|
||||||
return fmt.Errorf("unable to parse %q as a boolean: %w", value, err)
|
return fmt.Errorf("unable to parse %q as a boolean: %w", value, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
topLevelFlags.NoColor = new(bool)
|
noColor = new(bool)
|
||||||
*topLevelFlags.NoColor = boolVal
|
*noColor = boolVal
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
})
|
||||||
)
|
|
||||||
|
|
||||||
flag.StringVar(
|
|
||||||
&topLevelFlags.Pager,
|
|
||||||
"pager",
|
|
||||||
"",
|
|
||||||
"Specify your preferred pager to page through long outputs. This is disabled by default.",
|
|
||||||
)
|
|
||||||
|
|
||||||
flag.Usage = usageFunc(executor.CommandSummaryMap())
|
flag.Usage = usageFunc(executor.CommandSummaryMap())
|
||||||
|
|
||||||
|
@ -75,90 +62,107 @@ func run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 topLevelFlags.NoColor == nil {
|
if noColor == nil {
|
||||||
topLevelFlags.NoColor = new(bool)
|
noColor = new(bool)
|
||||||
if os.Getenv("NO_COLOR") != "" {
|
if os.Getenv("NO_COLOR") != "" {
|
||||||
*topLevelFlags.NoColor = true
|
*noColor = true
|
||||||
} else {
|
} else {
|
||||||
*topLevelFlags.NoColor = false
|
*noColor = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
command := flag.Arg(0)
|
command := flag.Arg(0)
|
||||||
args := flag.Args()[1:]
|
args := flag.Args()[1:]
|
||||||
|
|
||||||
|
printer := printer.NewPrinter(*noColor, pager, maxTerminalWidth)
|
||||||
|
|
||||||
executorMap := map[string]executor.Executor{
|
executorMap := map[string]executor.Executor{
|
||||||
executor.CommandAccept: executor.NewAcceptOrRejectExecutor(
|
executor.CommandAccept: executor.NewAcceptOrRejectExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandAccept,
|
executor.CommandAccept,
|
||||||
executor.CommandSummaryLookup(executor.CommandAccept),
|
executor.CommandSummaryLookup(executor.CommandAccept),
|
||||||
),
|
),
|
||||||
executor.CommandAdd: executor.NewAddExecutor(
|
executor.CommandAdd: executor.NewAddExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandAdd,
|
executor.CommandAdd,
|
||||||
executor.CommandSummaryLookup(executor.CommandAdd),
|
executor.CommandSummaryLookup(executor.CommandAdd),
|
||||||
),
|
),
|
||||||
executor.CommandBlock: executor.NewBlockOrUnblockExecutor(
|
executor.CommandBlock: executor.NewBlockOrUnblockExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandBlock,
|
executor.CommandBlock,
|
||||||
executor.CommandSummaryLookup(executor.CommandBlock),
|
executor.CommandSummaryLookup(executor.CommandBlock),
|
||||||
),
|
),
|
||||||
executor.CommandCreate: executor.NewCreateExecutor(
|
executor.CommandCreate: executor.NewCreateExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandCreate,
|
executor.CommandCreate,
|
||||||
executor.CommandSummaryLookup(executor.CommandCreate),
|
executor.CommandSummaryLookup(executor.CommandCreate),
|
||||||
),
|
),
|
||||||
executor.CommandDelete: executor.NewDeleteExecutor(
|
executor.CommandDelete: executor.NewDeleteExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandDelete,
|
executor.CommandDelete,
|
||||||
executor.CommandSummaryLookup(executor.CommandDelete),
|
executor.CommandSummaryLookup(executor.CommandDelete),
|
||||||
),
|
),
|
||||||
executor.CommandEdit: executor.NewEditExecutor(
|
executor.CommandEdit: executor.NewEditExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandEdit,
|
executor.CommandEdit,
|
||||||
executor.CommandSummaryLookup(executor.CommandEdit),
|
executor.CommandSummaryLookup(executor.CommandEdit),
|
||||||
),
|
),
|
||||||
executor.CommandFollow: executor.NewFollowOrUnfollowExecutor(
|
executor.CommandFollow: executor.NewFollowOrUnfollowExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandFollow,
|
executor.CommandFollow,
|
||||||
executor.CommandSummaryLookup(executor.CommandFollow),
|
executor.CommandSummaryLookup(executor.CommandFollow),
|
||||||
),
|
),
|
||||||
executor.CommandLogin: executor.NewLoginExecutor(
|
executor.CommandLogin: executor.NewLoginExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandLogin,
|
executor.CommandLogin,
|
||||||
executor.CommandSummaryLookup(executor.CommandLogin),
|
executor.CommandSummaryLookup(executor.CommandLogin),
|
||||||
),
|
),
|
||||||
executor.CommandReject: executor.NewAcceptOrRejectExecutor(
|
executor.CommandReject: executor.NewAcceptOrRejectExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandReject,
|
executor.CommandReject,
|
||||||
executor.CommandSummaryLookup(executor.CommandReject),
|
executor.CommandSummaryLookup(executor.CommandReject),
|
||||||
),
|
),
|
||||||
executor.CommandRemove: executor.NewRemoveExecutor(
|
executor.CommandRemove: executor.NewRemoveExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandRemove,
|
executor.CommandRemove,
|
||||||
executor.CommandSummaryLookup(executor.CommandRemove),
|
executor.CommandSummaryLookup(executor.CommandRemove),
|
||||||
),
|
),
|
||||||
executor.CommandSwitch: executor.NewSwitchExecutor(
|
executor.CommandSwitch: executor.NewSwitchExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandSwitch,
|
executor.CommandSwitch,
|
||||||
executor.CommandSummaryLookup(executor.CommandSwitch),
|
executor.CommandSummaryLookup(executor.CommandSwitch),
|
||||||
),
|
),
|
||||||
executor.CommandUnfollow: executor.NewFollowOrUnfollowExecutor(
|
executor.CommandUnfollow: executor.NewFollowOrUnfollowExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandUnfollow,
|
executor.CommandUnfollow,
|
||||||
executor.CommandSummaryLookup(executor.CommandUnfollow),
|
executor.CommandSummaryLookup(executor.CommandUnfollow),
|
||||||
),
|
),
|
||||||
executor.CommandUnblock: executor.NewBlockOrUnblockExecutor(
|
executor.CommandUnblock: executor.NewBlockOrUnblockExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandUnblock,
|
executor.CommandUnblock,
|
||||||
executor.CommandSummaryLookup(executor.CommandUnblock),
|
executor.CommandSummaryLookup(executor.CommandUnblock),
|
||||||
),
|
),
|
||||||
executor.CommandShow: executor.NewShowExecutor(
|
executor.CommandShow: executor.NewShowExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandShow,
|
executor.CommandShow,
|
||||||
executor.CommandSummaryLookup(executor.CommandShow),
|
executor.CommandSummaryLookup(executor.CommandShow),
|
||||||
),
|
),
|
||||||
executor.CommandVersion: executor.NewVersionExecutor(
|
executor.CommandVersion: executor.NewVersionExecutor(
|
||||||
|
printer,
|
||||||
executor.CommandVersion,
|
executor.CommandVersion,
|
||||||
executor.CommandSummaryLookup(executor.CommandVersion),
|
executor.CommandSummaryLookup(executor.CommandVersion),
|
||||||
binaryVersion,
|
binaryVersion,
|
||||||
|
@ -167,7 +171,8 @@ func run() error {
|
||||||
gitCommit,
|
gitCommit,
|
||||||
),
|
),
|
||||||
executor.CommandWhoami: executor.NewWhoAmIExecutor(
|
executor.CommandWhoami: executor.NewWhoAmIExecutor(
|
||||||
topLevelFlags,
|
printer,
|
||||||
|
configDir,
|
||||||
executor.CommandWhoami,
|
executor.CommandWhoami,
|
||||||
executor.CommandSummaryLookup(executor.CommandWhoami),
|
executor.CommandSummaryLookup(executor.CommandWhoami),
|
||||||
),
|
),
|
||||||
|
@ -175,13 +180,18 @@ func run() error {
|
||||||
|
|
||||||
exe, ok := executorMap[command]
|
exe, ok := executorMap[command]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
err := executor.UnknownCommandError{Command: command}
|
||||||
|
|
||||||
|
printer.PrintFailure(err.Error() + ".")
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
|
|
||||||
return executor.UnknownCommandError{Command: command}
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := executor.Execute(exe, args); err != nil {
|
if err := executor.Execute(exe, args); err != nil {
|
||||||
return fmt.Errorf("(%s) %w", command, err)
|
printer.PrintFailure("(" + command + ") " + err.Error() + ".")
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -39,21 +39,27 @@ func (g *Client) GetAccount(accountURI string) (model.Account, error) {
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Client) GetAccountRelationship(accountID string) (model.AccountRelationship, error) {
|
func (g *Client) GetAccountRelationship(accountID string) (*model.AccountRelationship, error) {
|
||||||
path := "/api/v1/accounts/relationships?id=" + accountID
|
path := "/api/v1/accounts/relationships?id=" + accountID
|
||||||
url := g.Authentication.Instance + path
|
url := g.Authentication.Instance + path
|
||||||
|
|
||||||
var relationships []model.AccountRelationship
|
var relationships []model.AccountRelationship
|
||||||
|
|
||||||
if err := g.sendRequest(http.MethodGet, url, nil, &relationships); err != nil {
|
if err := g.sendRequest(http.MethodGet, url, nil, &relationships); err != nil {
|
||||||
return model.AccountRelationship{}, fmt.Errorf("received an error after sending the request to get the account relationship: %w", err)
|
return nil, fmt.Errorf(
|
||||||
|
"received an error after sending the request to get the account relationship: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(relationships) != 1 {
|
if len(relationships) != 1 {
|
||||||
return model.AccountRelationship{}, fmt.Errorf("unexpected number of account relationships returned: want 1, got %d", len(relationships))
|
return nil, fmt.Errorf(
|
||||||
|
"unexpected number of account relationships returned: want 1, got %d",
|
||||||
|
len(relationships),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return relationships[0], nil
|
return &relationships[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type FollowAccountForm struct {
|
type FollowAccountForm struct {
|
||||||
|
|
|
@ -17,7 +17,7 @@ const (
|
||||||
listPath string = "/api/v1/lists"
|
listPath string = "/api/v1/lists"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g *Client) GetAllLists() (model.Lists, error) {
|
func (g *Client) GetAllLists() ([]model.List, error) {
|
||||||
url := g.Authentication.Instance + listPath
|
url := g.Authentication.Instance + listPath
|
||||||
|
|
||||||
var lists []model.List
|
var lists []model.List
|
||||||
|
|
|
@ -11,14 +11,14 @@ import (
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g *Client) GetUserPreferences() (model.Preferences, error) {
|
func (g *Client) GetUserPreferences() (*model.Preferences, error) {
|
||||||
url := g.Authentication.Instance + "/api/v1/preferences"
|
url := g.Authentication.Instance + "/api/v1/preferences"
|
||||||
|
|
||||||
var preferences model.Preferences
|
var preferences model.Preferences
|
||||||
|
|
||||||
if err := g.sendRequest(http.MethodGet, url, nil, &preferences); err != nil {
|
if err := g.sendRequest(http.MethodGet, url, nil, &preferences); err != nil {
|
||||||
return model.Preferences{}, fmt.Errorf("received an error after sending the request to get the user preferences: %w", err)
|
return nil, fmt.Errorf("received an error after sending the request to get the user preferences: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return preferences, nil
|
return &preferences, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,22 +9,25 @@ 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/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AcceptOrRejectExecutor struct {
|
type AcceptOrRejectExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
resourceType string
|
resourceType string
|
||||||
accountName string
|
accountName string
|
||||||
command string
|
command string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAcceptOrRejectExecutor(tlf TopLevelFlags, name, summary string) *AcceptOrRejectExecutor {
|
func NewAcceptOrRejectExecutor(enbasPrinter *printer.Printer, configDir, name, summary string) *AcceptOrRejectExecutor {
|
||||||
acceptExe := AcceptOrRejectExecutor{
|
acceptExe := AcceptOrRejectExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
topLevelFlags: tlf,
|
printer: enbasPrinter,
|
||||||
|
configDir: configDir,
|
||||||
command: name,
|
command: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +49,7 @@ func (a *AcceptOrRejectExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: a.resourceType}
|
return UnsupportedTypeError{resourceType: a.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(a.topLevelFlags.ConfigDir)
|
gtsClient, err := client.NewClientFromConfig(a.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +58,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.topLevelFlags.ConfigDir)
|
accountID, err := getAccountID(gtsClient, false, a.accountName, a.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +78,7 @@ func (a *AcceptOrRejectExecutor) acceptFollowRequest(gtsClient *client.Client, a
|
||||||
return fmt.Errorf("unable to accept the follow request: %w", err)
|
return fmt.Errorf("unable to accept the follow request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully accepted the follow request.")
|
a.printer.PrintSuccess("Successfully accepted the follow request.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -85,7 +88,7 @@ func (a *AcceptOrRejectExecutor) rejectFollowRequest(gtsClient *client.Client, a
|
||||||
return fmt.Errorf("unable to reject the follow request: %w", err)
|
return fmt.Errorf("unable to reject the follow request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully rejected the follow request.")
|
a.printer.PrintSuccess("Successfully rejected the follow request.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,14 @@ 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/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AddExecutor struct {
|
type AddExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
resourceType string
|
resourceType string
|
||||||
toResourceType string
|
toResourceType string
|
||||||
listID string
|
listID string
|
||||||
|
@ -26,13 +28,15 @@ type AddExecutor struct {
|
||||||
content string
|
content string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAddExecutor(tlf TopLevelFlags, name, summary string) *AddExecutor {
|
func NewAddExecutor(printer *printer.Printer, configDir, 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,
|
||||||
|
configDir: configDir,
|
||||||
accountNames: MultiStringFlagValue(emptyArr),
|
accountNames: MultiStringFlagValue(emptyArr),
|
||||||
topLevelFlags: tlf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addExe.StringVar(&addExe.resourceType, flagType, "", "Specify the resource type to add (e.g. account, note)")
|
addExe.StringVar(&addExe.resourceType, flagType, "", "Specify the resource type to add (e.g. account, note)")
|
||||||
|
@ -67,7 +71,7 @@ func (a *AddExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: a.toResourceType}
|
return UnsupportedTypeError{resourceType: a.toResourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(a.topLevelFlags.ConfigDir)
|
gtsClient, err := client.NewClientFromConfig(a.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -124,7 +128,7 @@ func (a *AddExecutor) addAccountsToList(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to add the accounts to the list: %w", err)
|
return fmt.Errorf("unable to add the accounts to the list: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully added the account(s) to the list.")
|
a.printer.PrintSuccess("Successfully added the account(s) to the list.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -150,7 +154,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.topLevelFlags.ConfigDir)
|
accountID, err := getAccountID(gtsClient, false, a.accountNames[0], a.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -166,7 +170,7 @@ func (a *AddExecutor) addNoteToAccount(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to add the private note to the account: %w", err)
|
return fmt.Errorf("unable to add the private note to the account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully added the private note to the account.")
|
a.printer.PrintSuccess("Successfully added the private note to the account.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -196,7 +200,7 @@ func (a *AddExecutor) addStatusToBookmarks(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to add the status to your bookmarks: %w", err)
|
return fmt.Errorf("unable to add the status to your bookmarks: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully added the status to your bookmarks.")
|
a.printer.PrintSuccess("Successfully added the status to your bookmarks.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -228,7 +232,7 @@ func (a *AddExecutor) addStarToStatus(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to add the %s to the status: %w", a.resourceType, err)
|
return fmt.Errorf("unable to add the %s to the status: %w", a.resourceType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Successfully added a %s to the status.\n", a.resourceType)
|
a.printer.PrintSuccess("Successfully added a " + a.resourceType + " to the status.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -238,7 +242,7 @@ func (a *AddExecutor) addBoostToStatus(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to add the boost to the status: %w", err)
|
return fmt.Errorf("unable to add the boost to the status: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully added the boost to the status.")
|
a.printer.PrintSuccess("Successfully added the boost to the status.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -285,7 +289,7 @@ func (a *AddExecutor) addVoteToPoll(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to add your vote(s) to the poll: %w", err)
|
return fmt.Errorf("unable to add your vote(s) to the poll: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully added your vote(s) to the poll.")
|
a.printer.PrintSuccess("Successfully added your vote(s) to the poll.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,22 +9,25 @@ 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/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BlockOrUnblockExecutor struct {
|
type BlockOrUnblockExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
resourceType string
|
resourceType string
|
||||||
accountName string
|
accountName string
|
||||||
command string
|
command string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockOrUnblockExecutor(tlf TopLevelFlags, name, summary string) *BlockOrUnblockExecutor {
|
func NewBlockOrUnblockExecutor(printer *printer.Printer, configDir, name, summary string) *BlockOrUnblockExecutor {
|
||||||
blockExe := BlockOrUnblockExecutor{
|
blockExe := BlockOrUnblockExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
topLevelFlags: tlf,
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
command: name,
|
command: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +49,7 @@ func (b *BlockOrUnblockExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: b.resourceType}
|
return UnsupportedTypeError{resourceType: b.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(b.topLevelFlags.ConfigDir)
|
gtsClient, err := client.NewClientFromConfig(b.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +58,7 @@ func (b *BlockOrUnblockExecutor) Execute() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BlockOrUnblockExecutor) blockOrUnblockAccount(gtsClient *client.Client) error {
|
func (b *BlockOrUnblockExecutor) blockOrUnblockAccount(gtsClient *client.Client) error {
|
||||||
accountID, err := getAccountID(gtsClient, false, b.accountName, b.topLevelFlags.ConfigDir)
|
accountID, err := getAccountID(gtsClient, false, b.accountName, b.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +78,7 @@ func (b *BlockOrUnblockExecutor) blockAccount(gtsClient *client.Client, accountI
|
||||||
return fmt.Errorf("unable to block the account: %w", err)
|
return fmt.Errorf("unable to block the account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully blocked the account.")
|
b.printer.PrintSuccess("Successfully blocked the account.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -85,7 +88,7 @@ func (b *BlockOrUnblockExecutor) unblockAccount(gtsClient *client.Client, accoun
|
||||||
return fmt.Errorf("unable to unblock the account: %w", err)
|
return fmt.Errorf("unable to unblock the account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully unblocked the account.")
|
b.printer.PrintSuccess("Successfully unblocked the account.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,14 @@ import (
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateExecutor struct {
|
type CreateExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
addPoll bool
|
addPoll bool
|
||||||
boostable bool
|
boostable bool
|
||||||
federated bool
|
federated bool
|
||||||
|
@ -26,6 +27,7 @@ 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
|
||||||
|
@ -39,11 +41,12 @@ type CreateExecutor struct {
|
||||||
pollOptions MultiStringFlagValue
|
pollOptions MultiStringFlagValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCreateExecutor(tlf TopLevelFlags, name, summary string) *CreateExecutor {
|
func NewCreateExecutor(printer *printer.Printer, configDir, name, summary string) *CreateExecutor {
|
||||||
createExe := CreateExecutor{
|
createExe := CreateExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
topLevelFlags: tlf,
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
||||||
|
@ -87,7 +90,7 @@ func (c *CreateExecutor) Execute() error {
|
||||||
return FlagNotSetError{flagText: flagType}
|
return FlagNotSetError{flagText: flagType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(c.topLevelFlags.ConfigDir)
|
gtsClient, err := client.NewClientFromConfig(c.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -125,8 +128,8 @@ func (c *CreateExecutor) createList(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to create the list: %w", err)
|
return fmt.Errorf("unable to create the list: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully created the following list:")
|
c.printer.PrintSuccess("Successfully created the following list:")
|
||||||
utilities.Display(list, *c.topLevelFlags.NoColor, c.topLevelFlags.Pager)
|
c.printer.PrintList(list)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -222,8 +225,8 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to create the status: %w", err)
|
return fmt.Errorf("unable to create the status: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully created the following status:")
|
c.printer.PrintSuccess("Successfully created the following status:")
|
||||||
utilities.Display(status, *c.topLevelFlags.NoColor, c.topLevelFlags.Pager)
|
c.printer.PrintStatus(status)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,20 +9,24 @@ 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/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeleteExecutor struct {
|
type DeleteExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
resourceType string
|
resourceType string
|
||||||
listID string
|
listID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDeleteExecutor(tlf TopLevelFlags, name, summary string) *DeleteExecutor {
|
func NewDeleteExecutor(printer *printer.Printer, configDir, name, summary string) *DeleteExecutor {
|
||||||
deleteExe := DeleteExecutor{
|
deleteExe := DeleteExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
topLevelFlags: tlf,
|
|
||||||
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteExe.StringVar(&deleteExe.resourceType, flagType, "", "Specify the type of resource to delete")
|
deleteExe.StringVar(&deleteExe.resourceType, flagType, "", "Specify the type of resource to delete")
|
||||||
|
@ -47,7 +51,7 @@ func (d *DeleteExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: d.resourceType}
|
return UnsupportedTypeError{resourceType: d.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(d.topLevelFlags.ConfigDir)
|
gtsClient, err := client.NewClientFromConfig(d.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -64,7 +68,7 @@ func (d *DeleteExecutor) deleteList(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to delete the list: %w", err)
|
return fmt.Errorf("unable to delete the list: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("The list was successfully deleted.")
|
d.printer.PrintSuccess("The list was successfully deleted.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,23 +10,26 @@ import (
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EditExecutor struct {
|
type EditExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
resourceType string
|
resourceType string
|
||||||
listID string
|
listID string
|
||||||
listTitle string
|
listTitle string
|
||||||
listRepliesPolicy string
|
listRepliesPolicy string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEditExecutor(tlf TopLevelFlags, name, summary string) *EditExecutor {
|
func NewEditExecutor(printer *printer.Printer, configDir, name, summary string) *EditExecutor {
|
||||||
editExe := EditExecutor{
|
editExe := EditExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
topLevelFlags: tlf,
|
|
||||||
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
editExe.StringVar(&editExe.resourceType, flagType, "", "Specify the type of resource to update")
|
editExe.StringVar(&editExe.resourceType, flagType, "", "Specify the type of resource to update")
|
||||||
|
@ -53,7 +56,7 @@ func (e *EditExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: e.resourceType}
|
return UnsupportedTypeError{resourceType: e.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(e.topLevelFlags.ConfigDir)
|
gtsClient, err := client.NewClientFromConfig(e.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -89,8 +92,8 @@ func (e *EditExecutor) editList(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to update the list: %w", err)
|
return fmt.Errorf("unable to update the list: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully updated the list.")
|
e.printer.PrintSuccess("Successfully updated the list.")
|
||||||
utilities.Display(updatedList, *e.topLevelFlags.NoColor, e.topLevelFlags.Pager)
|
e.printer.PrintList(updatedList)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,12 +51,6 @@ const (
|
||||||
flagVisibility = "visibility"
|
flagVisibility = "visibility"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TopLevelFlags struct {
|
|
||||||
ConfigDir string
|
|
||||||
NoColor *bool
|
|
||||||
Pager string
|
|
||||||
}
|
|
||||||
|
|
||||||
type MultiStringFlagValue []string
|
type MultiStringFlagValue []string
|
||||||
|
|
||||||
func (v *MultiStringFlagValue) String() string {
|
func (v *MultiStringFlagValue) String() string {
|
||||||
|
|
|
@ -9,12 +9,14 @@ 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/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FollowOrUnfollowExecutor struct {
|
type FollowOrUnfollowExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
resourceType string
|
resourceType string
|
||||||
accountName string
|
accountName string
|
||||||
showReposts bool
|
showReposts bool
|
||||||
|
@ -22,11 +24,12 @@ type FollowOrUnfollowExecutor struct {
|
||||||
action string
|
action string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFollowOrUnfollowExecutor(tlf TopLevelFlags, name, summary string) *FollowOrUnfollowExecutor {
|
func NewFollowOrUnfollowExecutor(printer *printer.Printer, configDir, name, summary string) *FollowOrUnfollowExecutor {
|
||||||
command := FollowOrUnfollowExecutor{
|
command := FollowOrUnfollowExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
topLevelFlags: tlf,
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
action: name,
|
action: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +53,7 @@ func (f *FollowOrUnfollowExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: f.resourceType}
|
return UnsupportedTypeError{resourceType: f.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(f.topLevelFlags.ConfigDir)
|
gtsClient, err := client.NewClientFromConfig(f.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -59,7 +62,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.topLevelFlags.ConfigDir)
|
accountID, err := getAccountID(gtsClient, false, f.accountName, f.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -85,7 +88,7 @@ func (f *FollowOrUnfollowExecutor) followAccount(gtsClient *client.Client, accou
|
||||||
return fmt.Errorf("unable to follow the account: %w", err)
|
return fmt.Errorf("unable to follow the account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("The follow request was sent successfully.")
|
f.printer.PrintSuccess("Successfully sent the follow request.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -95,7 +98,7 @@ func (f *FollowOrUnfollowExecutor) unfollowAccount(gtsClient *client.Client, acc
|
||||||
return fmt.Errorf("unable to unfollow the account: %w", err)
|
return fmt.Errorf("unable to unfollow the account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully unfollowed the account.")
|
f.printer.PrintSuccess("Successfully unfollowed the account.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,20 +11,24 @@ import (
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginExecutor struct {
|
type LoginExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
instance string
|
instance string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLoginExecutor(tlf TopLevelFlags, name, summary string) *LoginExecutor {
|
func NewLoginExecutor(printer *printer.Printer, configDir, name, summary string) *LoginExecutor {
|
||||||
command := LoginExecutor{
|
command := LoginExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
topLevelFlags: tlf,
|
|
||||||
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
instance: "",
|
instance: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,21 +70,17 @@ func (c *LoginExecutor) Execute() error {
|
||||||
|
|
||||||
utilities.OpenLink(consentPageURL)
|
utilities.OpenLink(consentPageURL)
|
||||||
|
|
||||||
consentMessageFormat := `
|
var builder strings.Builder
|
||||||
You'll need to sign into your GoToSocial's consent page in order to generate the out-of-band token to continue with
|
|
||||||
the application's login process. Your browser may have opened the link to the consent page already. If not, please
|
|
||||||
copy and paste the link below to your browser:
|
|
||||||
|
|
||||||
%s
|
builder.WriteString("\nYou'll need to sign into your GoToSocial's consent page in order to generate the out-of-band token to continue with the application's login process.")
|
||||||
|
builder.WriteString("\nYour browser may have opened the link to the consent page already. If not, please copy and paste the link below to your browser:")
|
||||||
|
builder.WriteString("\n\n" + consentPageURL)
|
||||||
|
builder.WriteString("\n\n" + "Once you have the code please copy and paste it below.")
|
||||||
|
builder.WriteString("\n" + "Out-of-band token: ")
|
||||||
|
|
||||||
Once you have the code please copy and paste it below.
|
c.printer.PrintInfo(builder.String())
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
fmt.Printf(consentMessageFormat, consentPageURL)
|
|
||||||
|
|
||||||
var code string
|
var code string
|
||||||
fmt.Print("Out-of-band token: ")
|
|
||||||
|
|
||||||
if _, err := fmt.Scanln(&code); err != nil {
|
if _, err := fmt.Scanln(&code); err != nil {
|
||||||
return fmt.Errorf("failed to read access code: %w", err)
|
return fmt.Errorf("failed to read access code: %w", err)
|
||||||
|
@ -95,12 +95,12 @@ Once you have the code please copy and paste it below.
|
||||||
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.topLevelFlags.ConfigDir, account.Username, gtsClient.Authentication)
|
loginName, err := config.SaveCredentials(c.configDir, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Successfully logged into %s\n", loginName)
|
c.printer.PrintSuccess("Successfully logged into " + loginName + ".")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,14 @@ 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/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RemoveExecutor struct {
|
type RemoveExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
resourceType string
|
resourceType string
|
||||||
fromResourceType string
|
fromResourceType string
|
||||||
listID string
|
listID string
|
||||||
|
@ -22,13 +24,15 @@ type RemoveExecutor struct {
|
||||||
accountNames MultiStringFlagValue
|
accountNames MultiStringFlagValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRemoveExecutor(tlf TopLevelFlags, name, summary string) *RemoveExecutor {
|
func NewRemoveExecutor(printer *printer.Printer, configDir, 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,
|
||||||
|
configDir: configDir,
|
||||||
accountNames: MultiStringFlagValue(emptyArr),
|
accountNames: MultiStringFlagValue(emptyArr),
|
||||||
topLevelFlags: tlf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeExe.StringVar(&removeExe.resourceType, flagType, "", "Specify the resource type to remove (e.g. account, note)")
|
removeExe.StringVar(&removeExe.resourceType, flagType, "", "Specify the resource type to remove (e.g. account, note)")
|
||||||
|
@ -59,7 +63,7 @@ func (r *RemoveExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: r.fromResourceType}
|
return UnsupportedTypeError{resourceType: r.fromResourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(r.topLevelFlags.ConfigDir)
|
gtsClient, err := client.NewClientFromConfig(r.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -107,7 +111,7 @@ func (r *RemoveExecutor) removeAccountsFromList(gtsClient *client.Client) error
|
||||||
return fmt.Errorf("unable to remove the accounts from the list: %w", err)
|
return fmt.Errorf("unable to remove the accounts from the list: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully removed the account(s) from the list.")
|
r.printer.PrintSuccess("Successfully removed the account(s) from the list.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -133,7 +137,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.topLevelFlags.ConfigDir)
|
accountID, err := getAccountID(gtsClient, false, r.accountNames[0], r.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -142,7 +146,7 @@ func (r *RemoveExecutor) removeNoteFromAccount(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to remove the private note from the account: %w", err)
|
return fmt.Errorf("unable to remove the private note from the account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully removed the private note from the account.")
|
r.printer.PrintSuccess("Successfully removed the private note from the account.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -172,7 +176,7 @@ func (r *RemoveExecutor) removeStatusFromBookmarks(gtsClient *client.Client) err
|
||||||
return fmt.Errorf("unable to remove the status from your bookmarks: %w", err)
|
return fmt.Errorf("unable to remove the status from your bookmarks: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully removed the status from your bookmarks.")
|
r.printer.PrintSuccess("Successfully removed the status from your bookmarks.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -204,7 +208,7 @@ func (r *RemoveExecutor) removeStarFromStatus(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to remove the %s from the status: %w", r.resourceType, err)
|
return fmt.Errorf("unable to remove the %s from the status: %w", r.resourceType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Successfully removed the %s from the status.\n", r.resourceType)
|
r.printer.PrintSuccess("Successfully removed the " + r.resourceType + " from the status.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -214,7 +218,7 @@ func (r *RemoveExecutor) removeBoostFromStatus(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to remove the boost from the status: %w", err)
|
return fmt.Errorf("unable to remove the boost from the status: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Successfully removed the boost from the status.")
|
r.printer.PrintSuccess("Successfully removed the boost from the status.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,16 +10,19 @@ import (
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ShowExecutor struct {
|
type ShowExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
topLevelFlags TopLevelFlags
|
|
||||||
|
printer *printer.Printer
|
||||||
myAccount bool
|
myAccount bool
|
||||||
skipAccountRelationship bool
|
skipAccountRelationship bool
|
||||||
showUserPreferences bool
|
showUserPreferences bool
|
||||||
showInBrowser bool
|
showInBrowser bool
|
||||||
|
configDir string
|
||||||
resourceType string
|
resourceType string
|
||||||
accountName string
|
accountName string
|
||||||
statusID string
|
statusID string
|
||||||
|
@ -30,10 +33,12 @@ type ShowExecutor struct {
|
||||||
limit int
|
limit int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShowExecutor(tlf TopLevelFlags, name, summary string) *ShowExecutor {
|
func NewShowExecutor(printer *printer.Printer, configDir, name, summary string) *ShowExecutor {
|
||||||
showExe := ShowExecutor{
|
showExe := ShowExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
topLevelFlags: tlf,
|
|
||||||
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
||||||
|
@ -80,7 +85,7 @@ func (s *ShowExecutor) Execute() error {
|
||||||
return UnsupportedTypeError{resourceType: s.resourceType}
|
return UnsupportedTypeError{resourceType: s.resourceType}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromConfig(s.topLevelFlags.ConfigDir)
|
gtsClient, err := client.NewClientFromConfig(s.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -94,7 +99,7 @@ func (s *ShowExecutor) showInstance(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to retrieve the instance details: %w", err)
|
return fmt.Errorf("unable to retrieve the instance details: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utilities.Display(instance, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintInstance(instance)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -106,7 +111,7 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
||||||
)
|
)
|
||||||
|
|
||||||
if s.myAccount {
|
if s.myAccount {
|
||||||
account, err = getMyAccount(gtsClient, s.topLevelFlags.ConfigDir)
|
account, err = getMyAccount(gtsClient, s.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -127,26 +132,27 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
utilities.Display(account, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
var (
|
||||||
|
relationship *model.AccountRelationship = nil
|
||||||
|
preferences *model.Preferences = nil
|
||||||
|
)
|
||||||
|
|
||||||
if !s.myAccount && !s.skipAccountRelationship {
|
if !s.myAccount && !s.skipAccountRelationship {
|
||||||
relationship, err := gtsClient.GetAccountRelationship(account.ID)
|
relationship, err = gtsClient.GetAccountRelationship(account.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to retrieve the relationship to this account: %w", err)
|
return fmt.Errorf("unable to retrieve the relationship to this account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utilities.Display(relationship, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.myAccount && s.showUserPreferences {
|
if s.myAccount && s.showUserPreferences {
|
||||||
preferences, err := gtsClient.GetUserPreferences()
|
preferences, err = gtsClient.GetUserPreferences()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to retrieve the user preferences: %w", err)
|
return fmt.Errorf("unable to retrieve the user preferences: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utilities.Display(preferences, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.printer.PrintAccount(account, relationship, preferences)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +172,7 @@ func (s *ShowExecutor) showStatus(gtsClient *client.Client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
utilities.Display(status, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintStatus(status)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -210,12 +216,12 @@ func (s *ShowExecutor) showTimeline(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(timeline.Statuses) == 0 {
|
if len(timeline.Statuses) == 0 {
|
||||||
fmt.Println("There are no statuses in this timeline.")
|
s.printer.PrintInfo("There are no statuses in this timeline.\n")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
utilities.Display(timeline, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintStatusList(timeline)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -244,7 +250,7 @@ func (s *ShowExecutor) showList(gtsClient *client.Client) error {
|
||||||
list.Accounts = accountMap
|
list.Accounts = accountMap
|
||||||
}
|
}
|
||||||
|
|
||||||
utilities.Display(list, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintList(list)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -256,18 +262,18 @@ func (s *ShowExecutor) showLists(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(lists) == 0 {
|
if len(lists) == 0 {
|
||||||
fmt.Println("You have no lists.")
|
s.printer.PrintInfo("You have no lists.\n")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
utilities.Display(lists, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintLists(lists)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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.topLevelFlags.ConfigDir)
|
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -278,16 +284,16 @@ func (s *ShowExecutor) showFollowers(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(followers.Accounts) > 0 {
|
if len(followers.Accounts) > 0 {
|
||||||
utilities.Display(followers, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintAccountList(followers)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("There are no followers for this account or the list is hidden.")
|
s.printer.PrintInfo("There are no followers for this account (or the list is hidden).\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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.topLevelFlags.ConfigDir)
|
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -298,9 +304,9 @@ func (s *ShowExecutor) showFollowing(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(following.Accounts) > 0 {
|
if len(following.Accounts) > 0 {
|
||||||
utilities.Display(following, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintAccountList(following)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("This account is not following anyone or the list is hidden.")
|
s.printer.PrintInfo("This account is not following anyone or the list is hidden.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -313,9 +319,9 @@ func (s *ShowExecutor) showBlocked(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(blocked.Accounts) > 0 {
|
if len(blocked.Accounts) > 0 {
|
||||||
utilities.Display(blocked, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintAccountList(blocked)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("You have no blocked accounts.")
|
s.printer.PrintInfo("You have no blocked accounts.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -328,9 +334,9 @@ func (s *ShowExecutor) showBookmarks(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bookmarks.Statuses) > 0 {
|
if len(bookmarks.Statuses) > 0 {
|
||||||
utilities.Display(bookmarks, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintStatusList(bookmarks)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("You have no bookmarks.")
|
s.printer.PrintInfo("You have no bookmarks.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -343,9 +349,9 @@ func (s *ShowExecutor) showLiked(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(liked.Statuses) > 0 {
|
if len(liked.Statuses) > 0 {
|
||||||
utilities.Display(liked, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintStatusList(liked)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("You have no %s statuses.\n", s.resourceType)
|
s.printer.PrintInfo("You have no " + s.resourceType + " statuses.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -358,9 +364,9 @@ func (s *ShowExecutor) showFollowRequests(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(accounts.Accounts) > 0 {
|
if len(accounts.Accounts) > 0 {
|
||||||
utilities.Display(accounts, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintAccountList(accounts)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("You have no follow requests.")
|
s.printer.PrintInfo("You have no follow requests.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -376,7 +382,7 @@ func (s *ShowExecutor) showPoll(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to retrieve the poll: %w", err)
|
return fmt.Errorf("unable to retrieve the poll: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utilities.Display(poll, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
|
s.printer.PrintPoll(poll)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,20 +9,23 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SwitchExecutor struct {
|
type SwitchExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
configDir string
|
||||||
toResourceType string
|
toResourceType string
|
||||||
accountName string
|
accountName string
|
||||||
|
printer *printer.Printer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSwitchExecutor(tlf TopLevelFlags, name, summary string) *SwitchExecutor {
|
func NewSwitchExecutor(printer *printer.Printer, configDir, name, summary string) *SwitchExecutor {
|
||||||
switchExe := SwitchExecutor{
|
switchExe := SwitchExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
topLevelFlags: tlf,
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
switchExe.StringVar(&switchExe.toResourceType, flagTo, "", "The account to switch to")
|
switchExe.StringVar(&switchExe.toResourceType, flagTo, "", "The account to switch to")
|
||||||
|
@ -51,11 +54,11 @@ func (s *SwitchExecutor) switchToAccount() error {
|
||||||
return NoAccountSpecifiedError{}
|
return NoAccountSpecifiedError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.UpdateCurrentAccount(s.accountName, s.topLevelFlags.ConfigDir); err != nil {
|
if err := config.UpdateCurrentAccount(s.accountName, s.configDir); 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("The current account is now set to %q.\n", s.accountName)
|
s.printer.PrintSuccess("The current account is now set to '" + s.accountName + "'.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,13 @@ package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"os"
|
|
||||||
"strings"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
"text/tabwriter"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type VersionExecutor struct {
|
type VersionExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
showFullVersion bool
|
showFullVersion bool
|
||||||
binaryVersion string
|
binaryVersion string
|
||||||
buildTime string
|
buildTime string
|
||||||
|
@ -20,9 +20,19 @@ type VersionExecutor struct {
|
||||||
gitCommit string
|
gitCommit string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVersionExecutor(name, summary, binaryVersion, buildTime, goVersion, gitCommit string) *VersionExecutor {
|
func NewVersionExecutor(
|
||||||
|
enbasPrinter *printer.Printer,
|
||||||
|
name,
|
||||||
|
summary,
|
||||||
|
binaryVersion,
|
||||||
|
buildTime,
|
||||||
|
goVersion,
|
||||||
|
gitCommit string,
|
||||||
|
) *VersionExecutor {
|
||||||
command := VersionExecutor{
|
command := VersionExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
|
||||||
|
printer: enbasPrinter,
|
||||||
binaryVersion: binaryVersion,
|
binaryVersion: binaryVersion,
|
||||||
buildTime: buildTime,
|
buildTime: buildTime,
|
||||||
goVersion: goVersion,
|
goVersion: goVersion,
|
||||||
|
@ -38,24 +48,7 @@ func NewVersionExecutor(name, summary, binaryVersion, buildTime, goVersion, gitC
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VersionExecutor) Execute() error {
|
func (v *VersionExecutor) Execute() error {
|
||||||
var builder strings.Builder
|
v.printer.PrintVersion(v.showFullVersion, v.binaryVersion, v.buildTime, v.goVersion, v.gitCommit)
|
||||||
|
|
||||||
if v.showFullVersion {
|
|
||||||
builder.WriteString("Enbas\n")
|
|
||||||
|
|
||||||
tableWriter := tabwriter.NewWriter(&builder, 0, 8, 0, '\t', 0)
|
|
||||||
|
|
||||||
tableWriter.Write([]byte(" Version:\t" + v.binaryVersion + "\n"))
|
|
||||||
tableWriter.Write([]byte(" Git commit:\t" + v.gitCommit + "\n"))
|
|
||||||
tableWriter.Write([]byte(" Go version:\t" + v.goVersion + "\n"))
|
|
||||||
tableWriter.Write([]byte(" Build date:\t" + v.buildTime + "\n"))
|
|
||||||
|
|
||||||
tableWriter.Flush()
|
|
||||||
} else {
|
|
||||||
builder.WriteString("Enbas " + v.binaryVersion + "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Stdout.WriteString(builder.String())
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,22 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WhoAmIExecutor struct {
|
type WhoAmIExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
|
|
||||||
topLevelFlags TopLevelFlags
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWhoAmIExecutor(tlf TopLevelFlags, name, summary string) *WhoAmIExecutor {
|
func NewWhoAmIExecutor(printer *printer.Printer, configDir, name, summary string) *WhoAmIExecutor {
|
||||||
whoExe := WhoAmIExecutor{
|
whoExe := WhoAmIExecutor{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
topLevelFlags: tlf,
|
|
||||||
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
whoExe.Usage = commandUsageFunc(name, summary, whoExe.FlagSet)
|
whoExe.Usage = commandUsageFunc(name, summary, whoExe.FlagSet)
|
||||||
|
@ -29,12 +33,12 @@ func NewWhoAmIExecutor(tlf TopLevelFlags, name, summary string) *WhoAmIExecutor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *WhoAmIExecutor) Execute() error {
|
func (c *WhoAmIExecutor) Execute() error {
|
||||||
config, err := config.NewCredentialsConfigFromFile(c.topLevelFlags.ConfigDir)
|
config, err := config.NewCredentialsConfigFromFile(c.configDir)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("You are logged in as %q.\n", config.CurrentAccount)
|
c.printer.PrintInfo("You are logged in as '" + config.CurrentAccount + "'.\n")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,7 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Account struct {
|
type Account struct {
|
||||||
|
@ -63,59 +60,6 @@ type Field struct {
|
||||||
VerifiedAt string `json:"verified_at"`
|
VerifiedAt string `json:"verified_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a Account) Display(noColor bool) string {
|
|
||||||
format := `
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s %d
|
|
||||||
%s %d
|
|
||||||
%s %d
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s %s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s`
|
|
||||||
|
|
||||||
metadata := ""
|
|
||||||
|
|
||||||
for _, field := range a.Fields {
|
|
||||||
metadata += fmt.Sprintf(
|
|
||||||
"\n %s: %s",
|
|
||||||
utilities.FieldFormat(noColor, field.Name),
|
|
||||||
utilities.ConvertHTMLToText(field.Value),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf(
|
|
||||||
format,
|
|
||||||
utilities.FullDisplayNameFormat(noColor, a.DisplayName, a.Acct),
|
|
||||||
utilities.HeaderFormat(noColor, "ACCOUNT ID:"),
|
|
||||||
a.ID,
|
|
||||||
utilities.HeaderFormat(noColor, "JOINED ON:"),
|
|
||||||
utilities.FormatDate(a.CreatedAt),
|
|
||||||
utilities.HeaderFormat(noColor, "STATS:"),
|
|
||||||
utilities.FieldFormat(noColor, "Followers:"), a.FollowersCount,
|
|
||||||
utilities.FieldFormat(noColor, "Following:"), a.FollowingCount,
|
|
||||||
utilities.FieldFormat(noColor, "Statuses:"), a.StatusCount,
|
|
||||||
utilities.HeaderFormat(noColor, "BIOGRAPHY:"),
|
|
||||||
utilities.WrapLines(utilities.ConvertHTMLToText(a.Note), "\n ", 80),
|
|
||||||
utilities.HeaderFormat(noColor, "METADATA:"),
|
|
||||||
metadata,
|
|
||||||
utilities.HeaderFormat(noColor, "ACCOUNT URL:"),
|
|
||||||
a.URL,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccountRelationship struct {
|
type AccountRelationship struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
PrivateNote string `json:"note"`
|
PrivateNote string `json:"note"`
|
||||||
|
@ -133,53 +77,6 @@ type AccountRelationship struct {
|
||||||
ShowingReblogs bool `json:"showing_reblogs"`
|
ShowingReblogs bool `json:"showing_reblogs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AccountRelationship) Display(noColor bool) string {
|
|
||||||
format := `
|
|
||||||
%s
|
|
||||||
%s: %t
|
|
||||||
%s: %t
|
|
||||||
%s: %t
|
|
||||||
%s: %t
|
|
||||||
%s: %t
|
|
||||||
%s: %t
|
|
||||||
%s: %t
|
|
||||||
%s: %t
|
|
||||||
%s: %t
|
|
||||||
%s: %t
|
|
||||||
%s: %t`
|
|
||||||
|
|
||||||
privateNoteFormat := `
|
|
||||||
%s
|
|
||||||
%s`
|
|
||||||
|
|
||||||
output := fmt.Sprintf(
|
|
||||||
format,
|
|
||||||
utilities.HeaderFormat(noColor, "YOUR RELATIONSHIP WITH THIS ACCOUNT:"),
|
|
||||||
utilities.FieldFormat(noColor, "Following"), a.Following,
|
|
||||||
utilities.FieldFormat(noColor, "Is following you"), a.FollowedBy,
|
|
||||||
utilities.FieldFormat(noColor, "A follow request was sent and is pending"), a.FollowRequested,
|
|
||||||
utilities.FieldFormat(noColor, "Received a pending follow request"), a.FollowRequestedBy,
|
|
||||||
utilities.FieldFormat(noColor, "Endorsed"), a.Endorsed,
|
|
||||||
utilities.FieldFormat(noColor, "Showing Reposts (boosts)"), a.ShowingReblogs,
|
|
||||||
utilities.FieldFormat(noColor, "Muted"), a.Muting,
|
|
||||||
utilities.FieldFormat(noColor, "Notifications muted"), a.MutingNotifications,
|
|
||||||
utilities.FieldFormat(noColor, "Blocking"), a.Blocking,
|
|
||||||
utilities.FieldFormat(noColor, "Is blocking you"), a.BlockedBy,
|
|
||||||
utilities.FieldFormat(noColor, "Blocking account's domain"), a.DomainBlocking,
|
|
||||||
)
|
|
||||||
|
|
||||||
if a.PrivateNote != "" {
|
|
||||||
output += "\n"
|
|
||||||
output += fmt.Sprintf(
|
|
||||||
privateNoteFormat,
|
|
||||||
utilities.HeaderFormat(noColor, "YOUR PRIVATE NOTE ABOUT THIS ACCOUNT:"),
|
|
||||||
utilities.WrapLines(a.PrivateNote, "\n ", 80),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccountListType int
|
type AccountListType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -193,36 +90,3 @@ type AccountList struct {
|
||||||
Type AccountListType
|
Type AccountListType
|
||||||
Accounts []Account
|
Accounts []Account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AccountList) Display(noColor bool) string {
|
|
||||||
output := "\n"
|
|
||||||
|
|
||||||
switch a.Type {
|
|
||||||
case AccountListFollowers:
|
|
||||||
output += utilities.HeaderFormat(noColor, "Followed by:")
|
|
||||||
case AccountListFollowing:
|
|
||||||
output += utilities.HeaderFormat(noColor, "Following:")
|
|
||||||
case AccountListBlockedAccount:
|
|
||||||
output += utilities.HeaderFormat(noColor, "Blocked accounts:")
|
|
||||||
case AccountListFollowRequests:
|
|
||||||
output += utilities.HeaderFormat(noColor, "Accounts that have requested to follow you:")
|
|
||||||
default:
|
|
||||||
output += utilities.HeaderFormat(noColor, "Accounts:")
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Type == AccountListBlockedAccount {
|
|
||||||
for i := range a.Accounts {
|
|
||||||
output += fmt.Sprintf(
|
|
||||||
"\n • %s (%s)",
|
|
||||||
a.Accounts[i].Acct,
|
|
||||||
a.Accounts[i].ID,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := range a.Accounts {
|
|
||||||
output += "\n • " + utilities.FullDisplayNameFormat(noColor, a.Accounts[i].DisplayName, a.Accounts[i].Acct)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,12 +4,6 @@
|
||||||
|
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
|
||||||
)
|
|
||||||
|
|
||||||
type InstanceV2 struct {
|
type InstanceV2 struct {
|
||||||
AccountDomain string `json:"account_domain"`
|
AccountDomain string `json:"account_domain"`
|
||||||
Configuration InstanceConfiguration `json:"configuration"`
|
Configuration InstanceConfiguration `json:"configuration"`
|
||||||
|
@ -116,48 +110,3 @@ type InstanceV2Usage struct {
|
||||||
type InstanceV2Users struct {
|
type InstanceV2Users struct {
|
||||||
ActiveMonth int `json:"active_month"`
|
ActiveMonth int `json:"active_month"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i InstanceV2) Display(noColor bool) string {
|
|
||||||
format := `
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s
|
|
||||||
Running GoToSocial %s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s %s
|
|
||||||
%s %s
|
|
||||||
%s %s
|
|
||||||
`
|
|
||||||
|
|
||||||
return fmt.Sprintf(
|
|
||||||
format,
|
|
||||||
utilities.HeaderFormat(noColor, "INSTANCE TITLE:"),
|
|
||||||
i.Title,
|
|
||||||
utilities.HeaderFormat(noColor, "INSTANCE DESCRIPTION:"),
|
|
||||||
utilities.WrapLines(i.DescriptionText, "\n ", 80),
|
|
||||||
utilities.HeaderFormat(noColor, "DOMAIN:"),
|
|
||||||
i.Domain,
|
|
||||||
utilities.HeaderFormat(noColor, "TERMS AND CONDITIONS:"),
|
|
||||||
utilities.WrapLines(i.TermsText, "\n ", 80),
|
|
||||||
utilities.HeaderFormat(noColor, "VERSION:"),
|
|
||||||
i.Version,
|
|
||||||
utilities.HeaderFormat(noColor, "CONTACT:"),
|
|
||||||
utilities.FieldFormat(noColor, "Name:"),
|
|
||||||
i.Contact.Account.DisplayName,
|
|
||||||
utilities.FieldFormat(noColor, "Username:"),
|
|
||||||
i.Contact.Account.Acct,
|
|
||||||
utilities.FieldFormat(noColor, "Email:"),
|
|
||||||
i.Contact.Email,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,8 +7,6 @@ package model
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ListRepliesPolicy int
|
type ListRepliesPolicy int
|
||||||
|
@ -107,53 +105,3 @@ type List struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Accounts map[string]string
|
Accounts map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l List) Display(noColor bool) string {
|
|
||||||
format := `
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
|
|
||||||
%s`
|
|
||||||
|
|
||||||
output := fmt.Sprintf(
|
|
||||||
format,
|
|
||||||
utilities.HeaderFormat(noColor, "LIST TITLE:"), l.Title,
|
|
||||||
utilities.HeaderFormat(noColor, "LIST ID:"), l.ID,
|
|
||||||
utilities.HeaderFormat(noColor, "REPLIES POLICY:"), l.RepliesPolicy,
|
|
||||||
utilities.HeaderFormat(noColor, "ADDED ACCOUNTS:"),
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(l.Accounts) > 0 {
|
|
||||||
for acct, name := range l.Accounts {
|
|
||||||
output += "\n • " + utilities.FullDisplayNameFormat(noColor, name, acct)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output += "\n None"
|
|
||||||
}
|
|
||||||
|
|
||||||
output += "\n"
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
type Lists []List
|
|
||||||
|
|
||||||
func (l Lists) Display(noColor bool) string {
|
|
||||||
output := "\n" + utilities.HeaderFormat(noColor, "LISTS")
|
|
||||||
|
|
||||||
for i := range l {
|
|
||||||
output += fmt.Sprintf(
|
|
||||||
"\n • %s (%s)",
|
|
||||||
l[i].Title,
|
|
||||||
l[i].ID,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,13 +5,7 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Poll struct {
|
type Poll struct {
|
||||||
|
@ -31,93 +25,3 @@ type PollOption struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
VotesCount int `json:"votes_count"`
|
VotesCount int `json:"votes_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Poll) Display(noColor bool) string {
|
|
||||||
var builder strings.Builder
|
|
||||||
|
|
||||||
indent := " "
|
|
||||||
|
|
||||||
builder.WriteString(
|
|
||||||
utilities.HeaderFormat(noColor, "POLL ID:") +
|
|
||||||
"\n" + indent + p.ID +
|
|
||||||
"\n\n" + utilities.HeaderFormat(noColor, "OPTIONS:"),
|
|
||||||
)
|
|
||||||
|
|
||||||
displayPollContent(&builder, p, noColor, indent)
|
|
||||||
|
|
||||||
builder.WriteString(
|
|
||||||
"\n\n" +
|
|
||||||
utilities.HeaderFormat(noColor, "MULTIPLE CHOICES ALLOWED:") +
|
|
||||||
"\n" + indent + strconv.FormatBool(p.Multiple) +
|
|
||||||
"\n\n" +
|
|
||||||
utilities.HeaderFormat(noColor, "YOU VOTED:") +
|
|
||||||
"\n" + indent + strconv.FormatBool(p.Voted),
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(p.OwnVotes) > 0 {
|
|
||||||
builder.WriteString("\n\n" + utilities.HeaderFormat(noColor, "YOUR VOTES:"))
|
|
||||||
|
|
||||||
for _, vote := range p.OwnVotes {
|
|
||||||
builder.WriteString("\n" + indent + "[" + strconv.Itoa(vote) + "] " + p.Options[vote].Title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.WriteString(
|
|
||||||
"\n\n" +
|
|
||||||
utilities.HeaderFormat(noColor, "EXPIRED:") +
|
|
||||||
"\n" + indent + strconv.FormatBool(p.Expired),
|
|
||||||
)
|
|
||||||
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func displayPollContent(writer io.StringWriter, poll Poll, noColor bool, indent string) {
|
|
||||||
for ind, option := range poll.Options {
|
|
||||||
var percentage int
|
|
||||||
var calculate float64
|
|
||||||
|
|
||||||
if poll.VotesCount == 0 {
|
|
||||||
percentage = 0
|
|
||||||
} else {
|
|
||||||
calculate = float64(option.VotesCount) / float64(poll.VotesCount)
|
|
||||||
percentage = int(math.Floor(100 * calculate))
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteString("\n\n" + indent + "[" + strconv.Itoa(ind) + "] " + option.Title)
|
|
||||||
drawPollMeter(writer, noColor, calculate, 80, indent)
|
|
||||||
|
|
||||||
writer.WriteString(
|
|
||||||
"\n" + indent + strconv.Itoa(option.VotesCount) + " votes " +
|
|
||||||
"(" + strconv.Itoa(percentage) + "%)",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteString(
|
|
||||||
"\n\n" +
|
|
||||||
indent + utilities.FieldFormat(noColor, "Total votes:") + " " + strconv.Itoa(poll.VotesCount) +
|
|
||||||
"\n" + indent + utilities.FieldFormat(noColor, "Poll ID:") + " " + poll.ID +
|
|
||||||
"\n" + indent + utilities.FieldFormat(noColor, "Poll is open until:") + " " + utilities.FormatTime(poll.ExpiredAt),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func drawPollMeter(writer io.StringWriter, noColor bool, calculated float64, limit int, indent string) {
|
|
||||||
numVoteBlocks := int(math.Floor(float64(limit) * calculated))
|
|
||||||
numBackgroundBlocks := limit - numVoteBlocks
|
|
||||||
blockChar := "\u2501"
|
|
||||||
voteBlockColor := "\033[32;1m"
|
|
||||||
backgroundBlockColor := "\033[90m"
|
|
||||||
|
|
||||||
if noColor {
|
|
||||||
voteBlockColor = "\033[0m"
|
|
||||||
|
|
||||||
if numVoteBlocks == 0 {
|
|
||||||
numVoteBlocks = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteString("\n" + indent + voteBlockColor + strings.Repeat(blockChar, numVoteBlocks) + "\033[0m")
|
|
||||||
|
|
||||||
if !noColor {
|
|
||||||
writer.WriteString(backgroundBlockColor + strings.Repeat(blockChar, numBackgroundBlocks) + "\033[0m")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,12 +4,6 @@
|
||||||
|
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Preferences struct {
|
type Preferences struct {
|
||||||
PostingDefaultVisibility string `json:"posting:default:visibility"`
|
PostingDefaultVisibility string `json:"posting:default:visibility"`
|
||||||
PostingDefaultSensitive bool `json:"posting:default:sensitive"`
|
PostingDefaultSensitive bool `json:"posting:default:sensitive"`
|
||||||
|
@ -18,19 +12,3 @@ type Preferences struct {
|
||||||
ReadingExpandSpoilers bool `json:"reading:expand:spoilers"`
|
ReadingExpandSpoilers bool `json:"reading:expand:spoilers"`
|
||||||
ReadingAutoplayGifs bool `json:"reading:autoplay:gifs"`
|
ReadingAutoplayGifs bool `json:"reading:autoplay:gifs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Preferences) Display(noColor bool) string {
|
|
||||||
format := `
|
|
||||||
%s
|
|
||||||
%s: %s
|
|
||||||
%s: %s
|
|
||||||
%s: %t`
|
|
||||||
|
|
||||||
return fmt.Sprintf(
|
|
||||||
format,
|
|
||||||
utilities.HeaderFormat(noColor, "YOUR PREFERENCES:"),
|
|
||||||
utilities.FieldFormat(noColor, "Default post language"), p.PostingDefaultLanguage,
|
|
||||||
utilities.FieldFormat(noColor, "Default post visibility"), p.PostingDefaultVisibility,
|
|
||||||
utilities.FieldFormat(noColor, "Mark posts as sensitive by default"), p.PostingDefaultSensitive,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,11 +5,7 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Status struct {
|
type Status struct {
|
||||||
|
@ -139,86 +135,7 @@ type MediaDimensions struct {
|
||||||
Width int `json:"width"`
|
Width int `json:"width"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Status) Display(noColor bool) string {
|
|
||||||
indent := " "
|
|
||||||
|
|
||||||
var builder strings.Builder
|
|
||||||
|
|
||||||
// The account information
|
|
||||||
builder.WriteString(utilities.FullDisplayNameFormat(noColor, s.Account.DisplayName, s.Account.Acct) + "\n\n")
|
|
||||||
|
|
||||||
// The content of the status.
|
|
||||||
builder.WriteString(utilities.HeaderFormat(noColor, "CONTENT:"))
|
|
||||||
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(s.Content), "\n ", 80))
|
|
||||||
|
|
||||||
// If a poll exists in a status, write the contents to the builder.
|
|
||||||
if s.Poll != nil {
|
|
||||||
displayPollContent(&builder, *s.Poll, noColor, indent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The ID of the status
|
|
||||||
builder.WriteString("\n\n" + utilities.HeaderFormat(noColor, "STATUS ID:") + "\n" + indent + s.ID)
|
|
||||||
|
|
||||||
// Status creation time
|
|
||||||
builder.WriteString("\n\n" + utilities.HeaderFormat(noColor, "CREATED AT:") + "\n" + indent + utilities.FormatTime(s.CreatedAt))
|
|
||||||
|
|
||||||
// Status stats
|
|
||||||
builder.WriteString(
|
|
||||||
"\n\n" +
|
|
||||||
utilities.HeaderFormat(noColor, "STATS:") +
|
|
||||||
"\n" + indent + utilities.FieldFormat(noColor, "Boosts: ") + strconv.Itoa(s.ReblogsCount) +
|
|
||||||
"\n" + indent + utilities.FieldFormat(noColor, "Likes: ") + strconv.Itoa(s.FavouritesCount) +
|
|
||||||
"\n" + indent + utilities.FieldFormat(noColor, "Replies: ") + strconv.Itoa(s.RepliesCount),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Status visibility
|
|
||||||
builder.WriteString("\n\n" + utilities.HeaderFormat(noColor, "VISIBILITY:") + "\n" + indent + s.Visibility.String())
|
|
||||||
|
|
||||||
// Status URL
|
|
||||||
builder.WriteString("\n\n" + utilities.HeaderFormat(noColor, "URL:") + "\n" + indent + s.URL)
|
|
||||||
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type StatusList struct {
|
type StatusList struct {
|
||||||
Name string
|
Name string
|
||||||
Statuses []Status
|
Statuses []Status
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s StatusList) Display(noColor bool) string {
|
|
||||||
var builder strings.Builder
|
|
||||||
|
|
||||||
separator := strings.Repeat("─", 80)
|
|
||||||
|
|
||||||
builder.WriteString(utilities.HeaderFormat(noColor, s.Name) + "\n")
|
|
||||||
|
|
||||||
for _, status := range s.Statuses {
|
|
||||||
builder.WriteString("\n" + utilities.FullDisplayNameFormat(noColor, status.Account.DisplayName, status.Account.Acct) + "\n")
|
|
||||||
|
|
||||||
statusID := status.ID
|
|
||||||
createdAt := status.CreatedAt
|
|
||||||
|
|
||||||
if status.Reblog != nil {
|
|
||||||
builder.WriteString("reposted this status from " + utilities.FullDisplayNameFormat(noColor, status.Reblog.Account.DisplayName, status.Reblog.Account.Acct) + "\n")
|
|
||||||
statusID = status.Reblog.ID
|
|
||||||
createdAt = status.Reblog.CreatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(status.Content), "\n", 80))
|
|
||||||
|
|
||||||
if status.Poll != nil {
|
|
||||||
displayPollContent(&builder, *status.Poll, noColor, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.WriteString(
|
|
||||||
"\n\n" +
|
|
||||||
utilities.FieldFormat(noColor, "Status ID:") + " " + statusID + "\t" +
|
|
||||||
utilities.FieldFormat(noColor, "Created at:") + " " + utilities.FormatTime(createdAt) +
|
|
||||||
"\n",
|
|
||||||
)
|
|
||||||
|
|
||||||
builder.WriteString(separator + "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
137
internal/printer/account.go
Normal file
137
internal/printer/account.go
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package printer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p Printer) PrintAccount(account model.Account, relationship *model.AccountRelationship, preferences *model.Preferences) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString("\n" + p.fullDisplayNameFormat(account.DisplayName, account.Acct))
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("ACCOUNT ID:"))
|
||||||
|
builder.WriteString("\n" + account.ID)
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("JOINED ON:"))
|
||||||
|
builder.WriteString("\n" + p.formatDate(account.CreatedAt))
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("STATS:"))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Followers:"))
|
||||||
|
builder.WriteString(" " + strconv.Itoa(account.FollowersCount))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Following:"))
|
||||||
|
builder.WriteString(" " + strconv.Itoa(account.FollowingCount))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Statuses:"))
|
||||||
|
builder.WriteString(" " + strconv.Itoa(account.StatusCount))
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("BIOGRAPHY:"))
|
||||||
|
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(account.Note), "\n", p.maxTerminalWidth))
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("METADATA:"))
|
||||||
|
|
||||||
|
for _, field := range account.Fields {
|
||||||
|
builder.WriteString("\n" + p.fieldFormat(field.Name) + ": " + field.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("ACCOUNT URL:"))
|
||||||
|
builder.WriteString("\n" + account.URL)
|
||||||
|
|
||||||
|
if relationship != nil {
|
||||||
|
builder.WriteString(p.accountRelationship(relationship))
|
||||||
|
}
|
||||||
|
|
||||||
|
if preferences != nil {
|
||||||
|
builder.WriteString(p.userPreferences(preferences))
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n\n")
|
||||||
|
|
||||||
|
p.print(builder.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) accountRelationship(relationship *model.AccountRelationship) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("YOUR RELATIONSHIP WITH THIS ACCOUNT:"))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Following:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.Following))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Is following you:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.FollowedBy))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("A follow request was sent and is pending:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.FollowRequested))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Received a pending follow request:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.FollowRequestedBy))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Endorsed:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.Endorsed))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Showing Reposts (boosts):"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.ShowingReblogs))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Muted:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.Muting))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Notifications muted:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.MutingNotifications))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Blocking:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.Blocking))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Is blocking you:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.BlockedBy))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Blocking account's domain:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(relationship.DomainBlocking))
|
||||||
|
|
||||||
|
if relationship.PrivateNote != "" {
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("YOUR PRIVATE NOTE ABOUT THIS ACCOUNT:"))
|
||||||
|
builder.WriteString("\n" + utilities.WrapLines(relationship.PrivateNote, "\n", p.maxTerminalWidth))
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) userPreferences(preferences *model.Preferences) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("YOUR PREFERENCES:"))
|
||||||
|
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Default post language:"))
|
||||||
|
builder.WriteString(" " + preferences.PostingDefaultLanguage)
|
||||||
|
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Default post visibility:"))
|
||||||
|
builder.WriteString(" " + preferences.PostingDefaultVisibility)
|
||||||
|
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Mark posts as sensitive by default:"))
|
||||||
|
builder.WriteString(" " + strconv.FormatBool(preferences.PostingDefaultSensitive))
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) PrintAccountList(list model.AccountList) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString("\n")
|
||||||
|
|
||||||
|
switch list.Type {
|
||||||
|
case model.AccountListFollowers:
|
||||||
|
builder.WriteString(p.headerFormat("Followed by:"))
|
||||||
|
case model.AccountListFollowing:
|
||||||
|
builder.WriteString(p.headerFormat("Following:"))
|
||||||
|
case model.AccountListBlockedAccount:
|
||||||
|
builder.WriteString(p.headerFormat("Blocked accounts:"))
|
||||||
|
case model.AccountListFollowRequests:
|
||||||
|
builder.WriteString(p.headerFormat("Accounts that have requested to follow you:"))
|
||||||
|
default:
|
||||||
|
builder.WriteString(p.headerFormat("Accounts:"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.Type == model.AccountListBlockedAccount {
|
||||||
|
for ind := range list.Accounts {
|
||||||
|
builder.WriteString("\n" + p.bullet + " " + list.Accounts[ind].Acct + " (" + list.Accounts[ind].ID + ")")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for ind := range list.Accounts {
|
||||||
|
builder.WriteString("\n" + p.bullet + " " + p.fullDisplayNameFormat(list.Accounts[ind].DisplayName, list.Accounts[ind].Acct))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n")
|
||||||
|
|
||||||
|
p.print(builder.String())
|
||||||
|
}
|
43
internal/printer/instance.go
Normal file
43
internal/printer/instance.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package printer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p Printer) PrintInstance(instance model.InstanceV2) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString("\n" + p.headerFormat("INSTANCE TITLE:"))
|
||||||
|
builder.WriteString("\n" + instance.Title)
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("INSTANCE DESCRIPTION:"))
|
||||||
|
builder.WriteString("\n" + utilities.WrapLines(instance.DescriptionText, "\n", p.maxTerminalWidth))
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("DOMAIN:"))
|
||||||
|
builder.WriteString("\n" + instance.Domain)
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("TERMS AND CONDITIONS:"))
|
||||||
|
builder.WriteString("\n" + utilities.WrapLines(instance.TermsText, "\n ", p.maxTerminalWidth))
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("VERSION:"))
|
||||||
|
builder.WriteString("\nRunning GoToSocial " + instance.Version)
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("CONTACT:"))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Name:"))
|
||||||
|
builder.WriteString(" " + instance.Contact.Account.DisplayName)
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Username:"))
|
||||||
|
builder.WriteString(" " + instance.Contact.Account.Acct)
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Email:"))
|
||||||
|
builder.WriteString(" " + instance.Contact.Email)
|
||||||
|
|
||||||
|
builder.WriteString("\n\n")
|
||||||
|
|
||||||
|
p.print(builder.String())
|
||||||
|
}
|
49
internal/printer/list.go
Normal file
49
internal/printer/list.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package printer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p Printer) PrintList(list model.List) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString("\n" + p.headerFormat("LIST TITLE:") + "\n")
|
||||||
|
builder.WriteString(list.Title + "\n\n")
|
||||||
|
builder.WriteString(p.headerFormat("LIST ID:") + "\n")
|
||||||
|
builder.WriteString(list.ID + "\n\n")
|
||||||
|
builder.WriteString(p.headerFormat("REPLIES POLICY:") + "\n")
|
||||||
|
builder.WriteString(list.RepliesPolicy.String() + "\n\n")
|
||||||
|
builder.WriteString(p.headerFormat("ADDED ACCOUNTS:"))
|
||||||
|
|
||||||
|
if len(list.Accounts) > 0 {
|
||||||
|
for acct, name := range list.Accounts {
|
||||||
|
builder.WriteString("\n" + p.bullet + " " + p.fullDisplayNameFormat(name, acct))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.WriteString("\n" + "None")
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n")
|
||||||
|
|
||||||
|
printToStdout(builder.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) PrintLists(lists []model.List) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString("\n" + p.headerFormat("LISTS"))
|
||||||
|
|
||||||
|
for i := range lists {
|
||||||
|
builder.WriteString("\n" + p.bullet + " " + lists[i].Title + " (" + lists[i].ID + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n")
|
||||||
|
|
||||||
|
printToStdout(builder.String())
|
||||||
|
}
|
95
internal/printer/poll.go
Normal file
95
internal/printer/poll.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.maxTerminalWidth) * votage))
|
||||||
|
numBackgroundBlocks := p.maxTerminalWidth - 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(p.pollMeterSymbol, numVoteBlocks) + p.theme.reset
|
||||||
|
|
||||||
|
if !p.noColor {
|
||||||
|
meter += backgroundBlockColor + strings.Repeat(p.pollMeterSymbol, numBackgroundBlocks) + p.theme.reset
|
||||||
|
}
|
||||||
|
|
||||||
|
return meter
|
||||||
|
}
|
185
internal/printer/printer.go
Normal file
185
internal/printer/printer.go
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package printer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
minTerminalWidth = 40
|
||||||
|
)
|
||||||
|
|
||||||
|
type theme struct {
|
||||||
|
reset string
|
||||||
|
boldblue string
|
||||||
|
boldmagenta string
|
||||||
|
green string
|
||||||
|
boldgreen string
|
||||||
|
grey string
|
||||||
|
red string
|
||||||
|
boldred string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Printer struct {
|
||||||
|
theme theme
|
||||||
|
noColor bool
|
||||||
|
maxTerminalWidth int
|
||||||
|
pager string
|
||||||
|
statusSeparator string
|
||||||
|
bullet string
|
||||||
|
pollMeterSymbol string
|
||||||
|
successSymbol string
|
||||||
|
failureSymbol string
|
||||||
|
dateFormat string
|
||||||
|
dateTimeFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrinter(
|
||||||
|
noColor bool,
|
||||||
|
pager string,
|
||||||
|
maxTerminalWidth int,
|
||||||
|
) *Printer {
|
||||||
|
theme := theme{
|
||||||
|
reset: "\033[0m",
|
||||||
|
boldblue: "\033[34;1m",
|
||||||
|
boldmagenta: "\033[35;1m",
|
||||||
|
green: "\033[32m",
|
||||||
|
boldgreen: "\033[32;1m",
|
||||||
|
grey: "\033[90m",
|
||||||
|
red: "\033[31m",
|
||||||
|
boldred: "\033[31;1m",
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxTerminalWidth < minTerminalWidth {
|
||||||
|
maxTerminalWidth = minTerminalWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Printer{
|
||||||
|
noColor: noColor,
|
||||||
|
maxTerminalWidth: maxTerminalWidth,
|
||||||
|
pager: pager,
|
||||||
|
statusSeparator: strings.Repeat("\u2501", maxTerminalWidth),
|
||||||
|
bullet: "\u2022",
|
||||||
|
pollMeterSymbol: "\u2501",
|
||||||
|
successSymbol: "\u2714",
|
||||||
|
failureSymbol: "\u2717",
|
||||||
|
dateFormat: "02 Jan 2006",
|
||||||
|
dateTimeFormat: "02 Jan 2006, 15:04 (MST)",
|
||||||
|
theme: theme,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) PrintSuccess(text string) {
|
||||||
|
success := p.theme.boldgreen + p.successSymbol + p.theme.reset
|
||||||
|
if p.noColor {
|
||||||
|
success = p.successSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
printToStdout(success + " " + text + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) PrintFailure(text string) {
|
||||||
|
failure := p.theme.boldred + p.failureSymbol + p.theme.reset
|
||||||
|
if p.noColor {
|
||||||
|
failure = p.failureSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
printToStderr(failure + " " + text + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) PrintInfo(text string) {
|
||||||
|
printToStdout(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) headerFormat(text string) string {
|
||||||
|
if p.noColor {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.theme.boldblue + text + p.theme.reset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) fieldFormat(text string) string {
|
||||||
|
if p.noColor {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.theme.green + text + p.theme.reset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) fullDisplayNameFormat(displayName, acct string) string {
|
||||||
|
// use this pattern to remove all emoji strings
|
||||||
|
pattern := regexp.MustCompile(`\s:[A-Za-z0-9_]*:`)
|
||||||
|
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
if p.noColor {
|
||||||
|
builder.WriteString(pattern.ReplaceAllString(displayName, ""))
|
||||||
|
} else {
|
||||||
|
builder.WriteString(p.theme.boldmagenta + pattern.ReplaceAllString(displayName, "") + p.theme.reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString(" (@" + acct + ")")
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) formatDate(date time.Time) string {
|
||||||
|
return date.Local().Format(p.dateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) formatDateTime(date time.Time) string {
|
||||||
|
return date.Local().Format(p.dateTimeFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) print(text string) {
|
||||||
|
if p.pager == "" {
|
||||||
|
printToStdout(text)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdSplit := strings.Split(p.pager, " ")
|
||||||
|
|
||||||
|
pager := new(exec.Cmd)
|
||||||
|
|
||||||
|
if len(cmdSplit) == 1 {
|
||||||
|
pager = exec.Command(cmdSplit[0]) //nolint:gosec
|
||||||
|
} else {
|
||||||
|
pager = exec.Command(cmdSplit[0], cmdSplit[1:]...) //nolint:gosec
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe, err := pager.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
printToStdout(text)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pager.Stdout = os.Stdout
|
||||||
|
pager.Stderr = os.Stderr
|
||||||
|
|
||||||
|
_ = pager.Start()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = pipe.Close()
|
||||||
|
_ = pager.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, _ = pipe.Write([]byte(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func printToStdout(text string) {
|
||||||
|
os.Stdout.WriteString(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printToStderr(text string) {
|
||||||
|
os.Stderr.WriteString(text)
|
||||||
|
}
|
97
internal/printer/status.go
Normal file
97
internal/printer/status.go
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package printer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p Printer) PrintStatus(status model.Status) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
// The account information
|
||||||
|
builder.WriteString("\n" + p.fullDisplayNameFormat(status.Account.DisplayName, status.Account.Acct))
|
||||||
|
|
||||||
|
// The content of the status.
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("CONTENT:"))
|
||||||
|
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(status.Content), "\n", p.maxTerminalWidth))
|
||||||
|
|
||||||
|
// If a poll exists in a status, write the contents to the builder.
|
||||||
|
if status.Poll != nil {
|
||||||
|
builder.WriteString(p.pollOptions(*status.Poll))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ID of the status
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("STATUS ID:"))
|
||||||
|
builder.WriteString("\n" + status.ID)
|
||||||
|
|
||||||
|
// Status creation time
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("CREATED AT:"))
|
||||||
|
builder.WriteString("\n" + p.formatDateTime(status.CreatedAt))
|
||||||
|
|
||||||
|
// Status stats
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("STATS:"))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Boosts: ") + strconv.Itoa(status.ReblogsCount))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Likes: ") + strconv.Itoa(status.FavouritesCount))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Replies: ") + strconv.Itoa(status.RepliesCount))
|
||||||
|
|
||||||
|
// Status visibility
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("VISIBILITY:"))
|
||||||
|
builder.WriteString("\n" + status.Visibility.String())
|
||||||
|
|
||||||
|
// Status URL
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("URL:"))
|
||||||
|
builder.WriteString("\n" + status.URL)
|
||||||
|
builder.WriteString("\n\n")
|
||||||
|
|
||||||
|
p.print(builder.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) PrintStatusList(list model.StatusList) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString(p.headerFormat(list.Name) + "\n")
|
||||||
|
|
||||||
|
for _, status := range list.Statuses {
|
||||||
|
builder.WriteString("\n" + p.fullDisplayNameFormat(status.Account.DisplayName, status.Account.Acct))
|
||||||
|
|
||||||
|
statusID := status.ID
|
||||||
|
createdAt := status.CreatedAt
|
||||||
|
|
||||||
|
if status.Reblog != nil {
|
||||||
|
builder.WriteString(utilities.WrapLines(
|
||||||
|
"\n"+
|
||||||
|
"reposted this status from "+
|
||||||
|
p.fullDisplayNameFormat(status.Reblog.Account.DisplayName, status.Reblog.Account.Acct),
|
||||||
|
"\n",
|
||||||
|
p.maxTerminalWidth,
|
||||||
|
))
|
||||||
|
|
||||||
|
statusID = status.Reblog.ID
|
||||||
|
createdAt = status.Reblog.CreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(status.Content), "\n", p.maxTerminalWidth))
|
||||||
|
|
||||||
|
if status.Poll != nil {
|
||||||
|
builder.WriteString(p.pollOptions(*status.Poll))
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString(
|
||||||
|
"\n\n" +
|
||||||
|
p.fieldFormat("Status ID:") + " " + statusID + "\t" +
|
||||||
|
p.fieldFormat("Created at:") + " " + p.formatDateTime(createdAt) +
|
||||||
|
"\n",
|
||||||
|
)
|
||||||
|
|
||||||
|
builder.WriteString(p.statusSeparator + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.print(builder.String())
|
||||||
|
}
|
33
internal/printer/version.go
Normal file
33
internal/printer/version.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package printer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p Printer) PrintVersion(showFullVersion bool, binaryVersion, buildTime, goVersion, gitCommit string) {
|
||||||
|
if !showFullVersion {
|
||||||
|
printToStdout("Enbas " + binaryVersion + "\n")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString(p.headerFormat("Enbas") + "\n\n")
|
||||||
|
|
||||||
|
tableWriter := tabwriter.NewWriter(&builder, 0, 4, 1, ' ', 0)
|
||||||
|
|
||||||
|
_, _ = 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()
|
||||||
|
|
||||||
|
printToStdout(builder.String())
|
||||||
|
}
|
|
@ -1,53 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
package utilities
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Displayer interface {
|
|
||||||
Display(noColor bool) string
|
|
||||||
}
|
|
||||||
|
|
||||||
func Display(displayer Displayer, noColor bool, pagerCommand string) {
|
|
||||||
if pagerCommand == "" {
|
|
||||||
os.Stdout.WriteString(displayer.Display(noColor) + "\n")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
split := strings.Split(pagerCommand, " ")
|
|
||||||
|
|
||||||
pager := new(exec.Cmd)
|
|
||||||
|
|
||||||
if len(split) == 1 {
|
|
||||||
pager = exec.Command(split[0]) //nolint:gosec
|
|
||||||
} else {
|
|
||||||
pager = exec.Command(split[0], split[1:]...) //nolint:gosec
|
|
||||||
}
|
|
||||||
|
|
||||||
pipe, err := pager.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
os.Stdout.WriteString(displayer.Display(noColor) + "\n")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pager.Stdout = os.Stdout
|
|
||||||
pager.Stderr = os.Stderr
|
|
||||||
|
|
||||||
_ = pager.Start()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = pipe.Close()
|
|
||||||
_ = pager.Wait()
|
|
||||||
}()
|
|
||||||
|
|
||||||
fmt.Fprintln(pipe, displayer.Display(noColor)+"\n")
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
package utilities
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
reset = "\033[0m"
|
|
||||||
boldblue = "\033[34;1m"
|
|
||||||
boldmagenta = "\033[35;1m"
|
|
||||||
green = "\033[32m"
|
|
||||||
)
|
|
||||||
|
|
||||||
func HeaderFormat(noColor bool, text string) string {
|
|
||||||
if noColor {
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
return boldblue + text + reset
|
|
||||||
}
|
|
||||||
|
|
||||||
func FieldFormat(noColor bool, text string) string {
|
|
||||||
if noColor {
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
return green + text + reset
|
|
||||||
}
|
|
||||||
|
|
||||||
func FullDisplayNameFormat(noColor bool, displayName, acct string) string {
|
|
||||||
// use this pattern to remove all emoji strings
|
|
||||||
pattern := regexp.MustCompile(`\s:[A-Za-z0-9_]*:`)
|
|
||||||
|
|
||||||
var builder strings.Builder
|
|
||||||
|
|
||||||
if noColor {
|
|
||||||
builder.WriteString(pattern.ReplaceAllString(displayName, ""))
|
|
||||||
} else {
|
|
||||||
builder.WriteString(boldmagenta + pattern.ReplaceAllString(displayName, "") + reset)
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.WriteString(" (@" + acct + ")")
|
|
||||||
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func FormatDate(date time.Time) string {
|
|
||||||
return date.Local().Format("02 Jan 2006")
|
|
||||||
}
|
|
||||||
|
|
||||||
func FormatTime(date time.Time) string {
|
|
||||||
return date.Local().Format("02 Jan 2006, 15:04 (MST)")
|
|
||||||
}
|
|
Loading…
Reference in a new issue