feat: private notes #11
8 changed files with 178 additions and 15 deletions
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
@ -41,6 +44,7 @@ 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
|
||||||
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -41,6 +41,7 @@ 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
|
||||||
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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:"),
|
||||||
|
|
Loading…
Reference in a new issue