From ecc8d51c098d75d6be0c918875b5bbe84dd293c2 Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Wed, 22 May 2024 11:58:56 +0100 Subject: [PATCH] feat: private notes This commit allows users to add or remove private notes from accounts. Changes: - feat: added functionality to add or remove private notes from accounts. - fix: added new error types or unsupported add and remove operations. - build: build Enbas without rebuilding all packages by default to speed up local builds for development. --- README.asciidoc | 17 +++++++++++ cmd/enbas/add.go | 50 ++++++++++++++++++++++++++++++-- cmd/enbas/errors.go | 18 ++++++++++++ cmd/enbas/main.go | 22 +++++++------- cmd/enbas/remove.go | 43 +++++++++++++++++++++++++-- internal/build/magefiles/mage.go | 20 ++++++++++++- internal/client/accounts.go | 22 ++++++++++++++ internal/model/account.go | 1 + 8 files changed, 178 insertions(+), 15 deletions(-) diff --git a/README.asciidoc b/README.asciidoc index 6fd09f6..82fb107 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -63,6 +63,23 @@ ENBAS_INSTALL_PREFIX=${HOME}/.local mage install This will install Enbas to `~/.local/bin/enbas`. +===== Environment variables you can use with Mage + +[%header,cols=2*] +|=== +|Environment Variable +|Description + +|`ENBAS_INSTALL_PREFIX` +|Set this to your preferred the installation prefix (default: `/usr/local`). + +|`ENBAS_BUILD_REBUILD_ALL` +|Set this to `1` to rebuild all packages even if they are already up-to-date. + +|`ENBAS_BUILD_VERBOSE` +|Set this to `1` to enable verbose logging when building the binary. +|=== + ==== Install with go If your `GOBIN` directory is included in your `PATH` then you can install Enbas with Go. diff --git a/cmd/enbas/add.go b/cmd/enbas/add.go index 23c155c..b860b75 100644 --- a/cmd/enbas/add.go +++ b/cmd/enbas/add.go @@ -1,6 +1,7 @@ package main import ( + "errors" "flag" "fmt" @@ -14,6 +15,7 @@ type addCommand struct { toResourceType string listID string accountNames accountNames + content string } func newAddCommand(name, summary string) *addCommand { @@ -28,6 +30,7 @@ func newAddCommand(name, summary string) *addCommand { command.StringVar(&command.toResourceType, addToFlag, "", "specify the target resource type to add to (e.g. list, account, etc)") command.StringVar(&command.listID, listIDFlag, "", "the ID of the list to add to") command.Var(&command.accountNames, accountNameFlag, "the name of the account to add to the resource") + command.StringVar(&command.content, contentFlag, "", "the content of the note") command.Usage = commandUsageFunc(name, summary, command.FlagSet) @@ -40,7 +43,8 @@ func (c *addCommand) Execute() error { } funcMap := map[string]func(*client.Client) error{ - listResource: c.addToList, + listResource: c.addToList, + accountResource: c.addToAccount, } doFunc, ok := funcMap[c.toResourceType] @@ -63,7 +67,10 @@ func (c *addCommand) addToList(gtsClient *client.Client) error { doFunc, ok := funcMap[c.resourceType] if !ok { - return unsupportedResourceTypeError{resourceType: c.resourceType} + return unsupportedAddOperationError{ + ResourceType: c.resourceType, + AddToResourceType: c.toResourceType, + } } return doFunc(gtsClient) @@ -97,3 +104,42 @@ func (c *addCommand) addAccountsToList(gtsClient *client.Client) error { return nil } + +func (c *addCommand) addToAccount(gtsClient *client.Client) error { + funcMap := map[string]func(*client.Client) error{ + noteResource: c.addNoteToAccount, + } + + doFunc, ok := funcMap[c.resourceType] + if !ok { + return unsupportedAddOperationError{ + ResourceType: c.resourceType, + AddToResourceType: c.toResourceType, + } + } + + return doFunc(gtsClient) +} + +func (c *addCommand) addNoteToAccount(gtsClient *client.Client) error { + if len(c.accountNames) != 1 { + return fmt.Errorf("unexpected number of accounts specified; want 1, got %d", len(c.accountNames)) + } + + accountID, err := getAccountID(gtsClient, false, c.accountNames[0]) + if err != nil { + return fmt.Errorf("received an error while getting the account ID; %w", err) + } + + if c.content == "" { + return errors.New("the note content should not be empty") + } + + if err := gtsClient.SetPrivateNote(accountID, c.content); err != nil { + return fmt.Errorf("unable to add the private note to the account; %w", err) + } + + fmt.Println("Successfully added the private note to the account.") + + return nil +} diff --git a/cmd/enbas/errors.go b/cmd/enbas/errors.go index e2be4f9..670113d 100644 --- a/cmd/enbas/errors.go +++ b/cmd/enbas/errors.go @@ -37,3 +37,21 @@ type noAccountSpecifiedError struct{} func (e noAccountSpecifiedError) Error() string { return "no account specified in this request" } + +type unsupportedAddOperationError struct { + ResourceType string + AddToResourceType string +} + +func (e unsupportedAddOperationError) Error() string { + return "adding '" + e.ResourceType + "' to '" + e.AddToResourceType + "' is not supported" +} + +type unsupportedRemoveOperationError struct { + ResourceType string + RemoveFromResourceType string +} + +func (e unsupportedRemoveOperationError) Error() string { + return "removing '" + e.ResourceType + "' from '" + e.RemoveFromResourceType + "' is not supported" +} diff --git a/cmd/enbas/main.go b/cmd/enbas/main.go index b036204..898da31 100644 --- a/cmd/enbas/main.go +++ b/cmd/enbas/main.go @@ -9,33 +9,35 @@ import ( const ( accountNameFlag = "account-name" addToFlag = "to" + contentFlag = "content" instanceFlag = "instance" + limitFlag = "limit" listIDFlag = "list-id" listTitleFlag = "list-title" listRepliesPolicyFlag = "list-replies-policy" myAccountFlag = "my-account" + notifyFlag = "notify" removeFromFlag = "from" resourceTypeFlag = "type" + showAccountRelationshipFlag = "show-account-relationship" + showUserPreferencesFlag = "show-preferences" + showRepostsFlag = "show-reposts" statusIDFlag = "status-id" tagFlag = "tag" timelineCategoryFlag = "timeline-category" - limitFlag = "limit" toAccountFlag = "to-account" - showRepostsFlag = "show-reposts" - notifyFlag = "notify" - showAccountRelationshipFlag = "show-account-relationship" - showUserPreferencesFlag = "show-preferences" ) const ( accountResource = "account" - instanceResource = "instance" - listResource = "list" - statusResource = "status" - timelineResource = "timeline" + blockedResource = "blocked" followersResource = "followers" followingResource = "following" - blockedResource = "blocked" + instanceResource = "instance" + listResource = "list" + noteResource = "note" + statusResource = "status" + timelineResource = "timeline" ) type Executor interface { diff --git a/cmd/enbas/remove.go b/cmd/enbas/remove.go index e5ebbde..f6e4c4a 100644 --- a/cmd/enbas/remove.go +++ b/cmd/enbas/remove.go @@ -40,7 +40,8 @@ func (c *removeCommand) Execute() error { } funcMap := map[string]func(*client.Client) error{ - listResource: c.removeFromList, + listResource: c.removeFromList, + accountResource: c.removeFromAccount, } doFunc, ok := funcMap[c.fromResourceType] @@ -63,7 +64,10 @@ func (c *removeCommand) removeFromList(gtsClient *client.Client) error { doFunc, ok := funcMap[c.resourceType] if !ok { - return unsupportedResourceTypeError{resourceType: c.resourceType} + return unsupportedRemoveOperationError{ + ResourceType: c.resourceType, + RemoveFromResourceType: c.fromResourceType, + } } return doFunc(gtsClient) @@ -97,3 +101,38 @@ func (c *removeCommand) removeAccountsFromList(gtsClient *client.Client) error { return nil } + +func (c *removeCommand) removeFromAccount(gtsClient *client.Client) error { + funcMap := map[string]func(*client.Client) error{ + noteResource: c.removeNoteFromAccount, + } + + doFunc, ok := funcMap[c.resourceType] + if !ok { + return unsupportedRemoveOperationError{ + ResourceType: c.resourceType, + RemoveFromResourceType: c.fromResourceType, + } + } + + return doFunc(gtsClient) +} + +func (c *removeCommand) removeNoteFromAccount(gtsClient *client.Client) error { + if len(c.accountNames) != 1 { + return fmt.Errorf("unexpected number of accounts specified; want 1, got %d", len(c.accountNames)) + } + + accountID, err := getAccountID(gtsClient, false, c.accountNames[0]) + if err != nil { + return fmt.Errorf("received an error while getting the account ID; %w", err) + } + + if err := gtsClient.SetPrivateNote(accountID, ""); err != nil { + return fmt.Errorf("unable to remove the private note from the account; %w", err) + } + + fmt.Println("Successfully removed the private note from the account.") + + return nil +} diff --git a/internal/build/magefiles/mage.go b/internal/build/magefiles/mage.go index af17039..3271229 100644 --- a/internal/build/magefiles/mage.go +++ b/internal/build/magefiles/mage.go @@ -19,6 +19,8 @@ const ( envInstallPrefix = "ENBAS_INSTALL_PREFIX" envTestVerbose = "ENBAS_TEST_VERBOSE" envTestCover = "ENBAS_TEST_COVER" + envBuildRebuildAll = "ENBAS_BUILD_REBUILD_ALL" + envBuildVerbose = "ENBAS_BUILD_VERBOSE" ) var Default = Build @@ -56,13 +58,29 @@ func Lint() error { } // Build build the executable. +// To rebuild packages that are already up-to-date set ENBAS_BUILD_REBUILD_ALL=1 +// To enable verbose mode set ENBAS_BUILD_VERBOSE=1 func Build() error { if err := changeToProjectRoot(); err != nil { return fmt.Errorf("unable to change to the project's root directory; %w", err) } + main := "./cmd/" + binary flags := ldflags() - return sh.Run("go", "build", "-ldflags="+flags, "-a", "-o", binary, "./cmd/enbas") + build := sh.RunCmd("go", "build") + args := []string{"-ldflags=" + flags, "-o", binary} + + if os.Getenv(envBuildRebuildAll) == "1" { + args = append(args, "-a") + } + + if os.Getenv(envBuildVerbose) == "1" { + args = append(args, "-v") + } + + args = append(args, main) + + return build(args...) } // Install install the executable. diff --git a/internal/client/accounts.go b/internal/client/accounts.go index 490e8a3..0513eb2 100644 --- a/internal/client/accounts.go +++ b/internal/client/accounts.go @@ -158,3 +158,25 @@ func (g *Client) GetBlockedAccounts(limit int) (model.AccountList, error) { return blocked, nil } + +func (g *Client) SetPrivateNote(accountID, note string) error { + form := struct { + Comment string `json:"comment"` + }{ + Comment: note, + } + + data, err := json.Marshal(form) + if err != nil { + return fmt.Errorf("unable to marshal the form; %w", err) + } + + requestBody := bytes.NewBuffer(data) + url := g.Authentication.Instance + fmt.Sprintf("/api/v1/accounts/%s/note", accountID) + + if err := g.sendRequest(http.MethodPost, url, requestBody, nil); err != nil { + return fmt.Errorf("received an error after sending the request to set the private note; %w", err) + } + + return nil +} diff --git a/internal/model/account.go b/internal/model/account.go index bd7bab0..6264e39 100644 --- a/internal/model/account.go +++ b/internal/model/account.go @@ -166,6 +166,7 @@ func (a AccountRelationship) String() string { ) if a.PrivateNote != "" { + output += "\n" output += fmt.Sprintf( privateNoteFormat, utilities.HeaderFormat("YOUR PRIVATE NOTE ABOUT THIS ACCOUNT:"), -- 2.45.2