feat: private notes #11

Manually merged
dananglin merged 1 commit from private-notes into main 2024-05-22 12:05:05 +01:00
8 changed files with 178 additions and 15 deletions

View file

@ -63,6 +63,23 @@ ENBAS_INSTALL_PREFIX=${HOME}/.local mage install
This will install Enbas to `~/.local/bin/enbas`. 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 ==== Install with go
If your `GOBIN` directory is included in your `PATH` then you can install Enbas with Go. If your `GOBIN` directory is included in your `PATH` then you can install Enbas with Go.

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"flag" "flag"
"fmt" "fmt"
@ -14,6 +15,7 @@ type addCommand struct {
toResourceType string toResourceType string
listID string listID string
accountNames accountNames accountNames accountNames
content string
} }
func newAddCommand(name, summary string) *addCommand { 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.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.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.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) command.Usage = commandUsageFunc(name, summary, command.FlagSet)
@ -40,7 +43,8 @@ func (c *addCommand) Execute() error {
} }
funcMap := map[string]func(*client.Client) error{ funcMap := map[string]func(*client.Client) error{
listResource: c.addToList, listResource: c.addToList,
accountResource: c.addToAccount,
} }
doFunc, ok := funcMap[c.toResourceType] doFunc, ok := funcMap[c.toResourceType]
@ -63,7 +67,10 @@ func (c *addCommand) addToList(gtsClient *client.Client) error {
doFunc, ok := funcMap[c.resourceType] doFunc, ok := funcMap[c.resourceType]
if !ok { if !ok {
return unsupportedResourceTypeError{resourceType: c.resourceType} return unsupportedAddOperationError{
ResourceType: c.resourceType,
AddToResourceType: c.toResourceType,
}
} }
return doFunc(gtsClient) return doFunc(gtsClient)
@ -97,3 +104,42 @@ func (c *addCommand) addAccountsToList(gtsClient *client.Client) error {
return nil 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
}

View file

@ -37,3 +37,21 @@ type noAccountSpecifiedError struct{}
func (e noAccountSpecifiedError) Error() string { func (e noAccountSpecifiedError) Error() string {
return "no account specified in this request" 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"
}

View file

@ -9,33 +9,35 @@ import (
const ( const (
accountNameFlag = "account-name" accountNameFlag = "account-name"
addToFlag = "to" addToFlag = "to"
contentFlag = "content"
instanceFlag = "instance" instanceFlag = "instance"
limitFlag = "limit"
listIDFlag = "list-id" listIDFlag = "list-id"
listTitleFlag = "list-title" listTitleFlag = "list-title"
listRepliesPolicyFlag = "list-replies-policy" listRepliesPolicyFlag = "list-replies-policy"
myAccountFlag = "my-account" myAccountFlag = "my-account"
notifyFlag = "notify"
removeFromFlag = "from" removeFromFlag = "from"
resourceTypeFlag = "type" resourceTypeFlag = "type"
showAccountRelationshipFlag = "show-account-relationship"
showUserPreferencesFlag = "show-preferences"
showRepostsFlag = "show-reposts"
statusIDFlag = "status-id" statusIDFlag = "status-id"
tagFlag = "tag" tagFlag = "tag"
timelineCategoryFlag = "timeline-category" timelineCategoryFlag = "timeline-category"
limitFlag = "limit"
toAccountFlag = "to-account" toAccountFlag = "to-account"
showRepostsFlag = "show-reposts"
notifyFlag = "notify"
showAccountRelationshipFlag = "show-account-relationship"
showUserPreferencesFlag = "show-preferences"
) )
const ( const (
accountResource = "account" accountResource = "account"
instanceResource = "instance" blockedResource = "blocked"
listResource = "list"
statusResource = "status"
timelineResource = "timeline"
followersResource = "followers" followersResource = "followers"
followingResource = "following" followingResource = "following"
blockedResource = "blocked" instanceResource = "instance"
listResource = "list"
noteResource = "note"
statusResource = "status"
timelineResource = "timeline"
) )
type Executor interface { type Executor interface {

View file

@ -40,7 +40,8 @@ func (c *removeCommand) Execute() error {
} }
funcMap := map[string]func(*client.Client) error{ funcMap := map[string]func(*client.Client) error{
listResource: c.removeFromList, listResource: c.removeFromList,
accountResource: c.removeFromAccount,
} }
doFunc, ok := funcMap[c.fromResourceType] doFunc, ok := funcMap[c.fromResourceType]
@ -63,7 +64,10 @@ func (c *removeCommand) removeFromList(gtsClient *client.Client) error {
doFunc, ok := funcMap[c.resourceType] doFunc, ok := funcMap[c.resourceType]
if !ok { if !ok {
return unsupportedResourceTypeError{resourceType: c.resourceType} return unsupportedRemoveOperationError{
ResourceType: c.resourceType,
RemoveFromResourceType: c.fromResourceType,
}
} }
return doFunc(gtsClient) return doFunc(gtsClient)
@ -97,3 +101,38 @@ func (c *removeCommand) removeAccountsFromList(gtsClient *client.Client) error {
return nil 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
}

View file

@ -19,6 +19,8 @@ const (
envInstallPrefix = "ENBAS_INSTALL_PREFIX" envInstallPrefix = "ENBAS_INSTALL_PREFIX"
envTestVerbose = "ENBAS_TEST_VERBOSE" envTestVerbose = "ENBAS_TEST_VERBOSE"
envTestCover = "ENBAS_TEST_COVER" envTestCover = "ENBAS_TEST_COVER"
envBuildRebuildAll = "ENBAS_BUILD_REBUILD_ALL"
envBuildVerbose = "ENBAS_BUILD_VERBOSE"
) )
var Default = Build var Default = Build
@ -56,13 +58,29 @@ func Lint() error {
} }
// Build build the executable. // 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 { func Build() error {
if err := changeToProjectRoot(); err != nil { if err := changeToProjectRoot(); err != nil {
return fmt.Errorf("unable to change to the project's root directory; %w", err) return fmt.Errorf("unable to change to the project's root directory; %w", err)
} }
main := "./cmd/" + binary
flags := ldflags() 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. // Install install the executable.

View file

@ -158,3 +158,25 @@ func (g *Client) GetBlockedAccounts(limit int) (model.AccountList, error) {
return blocked, nil 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
}

View file

@ -166,6 +166,7 @@ func (a AccountRelationship) String() string {
) )
if a.PrivateNote != "" { if a.PrivateNote != "" {
output += "\n"
output += fmt.Sprintf( output += fmt.Sprintf(
privateNoteFormat, privateNoteFormat,
utilities.HeaderFormat("YOUR PRIVATE NOTE ABOUT THIS ACCOUNT:"), utilities.HeaderFormat("YOUR PRIVATE NOTE ABOUT THIS ACCOUNT:"),