feat: add and remove accounts from a list. #6
9 changed files with 341 additions and 21 deletions
73
cmd/enbas/add.go
Normal file
73
cmd/enbas/add.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||
)
|
||||
|
||||
type addCommand struct {
|
||||
*flag.FlagSet
|
||||
|
||||
toResourceType string
|
||||
listID string
|
||||
accountIDs accountIDs
|
||||
}
|
||||
|
||||
func newAddCommand(name, summary string) *addCommand {
|
||||
emptyArr := make([]string, 0, 3)
|
||||
|
||||
command := addCommand{
|
||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||
accountIDs: accountIDs(emptyArr),
|
||||
}
|
||||
|
||||
command.StringVar(&command.toResourceType, addToFlag, "", "specify the type of resource to add to")
|
||||
command.StringVar(&command.listID, listIDFlag, "", "the ID of the list to add to")
|
||||
command.Var(&command.accountIDs, accountIDFlag, "the ID of the account to add to the list")
|
||||
|
||||
command.Usage = commandUsageFunc(name, summary, command.FlagSet)
|
||||
|
||||
return &command
|
||||
}
|
||||
|
||||
func (c *addCommand) Execute() error {
|
||||
if c.toResourceType == "" {
|
||||
return flagNotSetError{flagText: "add-to"}
|
||||
}
|
||||
|
||||
funcMap := map[string]func(*client.Client) error{
|
||||
listResource: c.addAccountsToList,
|
||||
}
|
||||
|
||||
doFunc, ok := funcMap[c.toResourceType]
|
||||
if !ok {
|
||||
return unsupportedResourceTypeError{resourceType: c.toResourceType}
|
||||
}
|
||||
|
||||
gtsClient, err := client.NewClientFromConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create the GoToSocial client; %w", err)
|
||||
}
|
||||
|
||||
return doFunc(gtsClient)
|
||||
}
|
||||
|
||||
func (c *addCommand) addAccountsToList(gtsClient *client.Client) error {
|
||||
if c.listID == "" {
|
||||
return flagNotSetError{flagText: listIDFlag}
|
||||
}
|
||||
|
||||
if len(c.accountIDs) == 0 {
|
||||
return noAccountIDsSpecifiedError{}
|
||||
}
|
||||
|
||||
if err := gtsClient.AddAccountsToList(c.listID, []string(c.accountIDs)); err != nil {
|
||||
return fmt.Errorf("unable to add the accounts to the list; %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Successfully added the account(s) to the list.")
|
||||
|
||||
return nil
|
||||
}
|
|
@ -23,3 +23,17 @@ type invalidTimelineCategoryError struct {
|
|||
func (e invalidTimelineCategoryError) Error() string {
|
||||
return "'" + e.category + "' is not a valid timeline category (please choose home, public, tag or list)"
|
||||
}
|
||||
|
||||
type unknownSubcommandError struct {
|
||||
subcommand string
|
||||
}
|
||||
|
||||
func (e unknownSubcommandError) Error() string {
|
||||
return "unknown subcommand '" + e.subcommand + "'"
|
||||
}
|
||||
|
||||
type noAccountIDsSpecifiedError struct{}
|
||||
|
||||
func (e noAccountIDsSpecifiedError) Error() string {
|
||||
return "no account IDs specified"
|
||||
}
|
||||
|
|
17
cmd/enbas/flags.go
Normal file
17
cmd/enbas/flags.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package main
|
||||
|
||||
import "strings"
|
||||
|
||||
type accountIDs []string
|
||||
|
||||
func (a *accountIDs) String() string {
|
||||
return strings.Join(*a, ", ")
|
||||
}
|
||||
|
||||
func (a *accountIDs) Set(value string) error {
|
||||
if len(value) > 0 {
|
||||
*a = append(*a, value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -8,11 +8,14 @@ import (
|
|||
|
||||
const (
|
||||
accountFlag = "account"
|
||||
accountIDFlag = "account-id"
|
||||
addToFlag = "add-to"
|
||||
instanceFlag = "instance"
|
||||
listIDFlag = "list-id"
|
||||
listTitleFlag = "list-title"
|
||||
listRepliesPolicyFlag = "list-replies-policy"
|
||||
myAccountFlag = "my-account"
|
||||
removeFromFlag = "remove-from"
|
||||
resourceTypeFlag = "type"
|
||||
statusIDFlag = "status-id"
|
||||
tagFlag = "tag"
|
||||
|
@ -52,6 +55,8 @@ func run() error {
|
|||
deleteResource string = "delete"
|
||||
updateResource string = "update"
|
||||
whoami string = "whoami"
|
||||
add string = "add"
|
||||
remove string = "remove"
|
||||
)
|
||||
|
||||
summaries := map[string]string{
|
||||
|
@ -63,6 +68,8 @@ func run() error {
|
|||
deleteResource: "delete a specific resource",
|
||||
updateResource: "update a specific resource",
|
||||
whoami: "print the account that you are currently logged in to",
|
||||
add: "add a resource to another resource",
|
||||
remove: "remove a resource from another resource",
|
||||
}
|
||||
|
||||
flag.Usage = enbasUsageFunc(summaries)
|
||||
|
@ -97,9 +104,14 @@ func run() error {
|
|||
executor = newUpdateCommand(updateResource, summaries[updateResource])
|
||||
case whoami:
|
||||
executor = newWhoAmICommand(whoami, summaries[whoami])
|
||||
case add:
|
||||
executor = newAddCommand(add, summaries[add])
|
||||
case remove:
|
||||
executor = newRemoveCommand(remove, summaries[remove])
|
||||
default:
|
||||
flag.Usage()
|
||||
return fmt.Errorf("unknown subcommand %q", subcommand)
|
||||
|
||||
return unknownSubcommandError{subcommand}
|
||||
}
|
||||
|
||||
if err := executor.Parse(args); err != nil {
|
||||
|
|
73
cmd/enbas/remove.go
Normal file
73
cmd/enbas/remove.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||
)
|
||||
|
||||
type removeCommand struct {
|
||||
*flag.FlagSet
|
||||
|
||||
fromResourceType string
|
||||
listID string
|
||||
accountIDs accountIDs
|
||||
}
|
||||
|
||||
func newRemoveCommand(name, summary string) *removeCommand {
|
||||
emptyArr := make([]string, 0, 3)
|
||||
|
||||
command := removeCommand{
|
||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||
accountIDs: accountIDs(emptyArr),
|
||||
}
|
||||
|
||||
command.StringVar(&command.fromResourceType, removeFromFlag, "", "specify the type of resource to remove from")
|
||||
command.StringVar(&command.listID, listIDFlag, "", "the ID of the list to remove from")
|
||||
command.Var(&command.accountIDs, accountIDFlag, "the ID of the account to remove from the list")
|
||||
|
||||
command.Usage = commandUsageFunc(name, summary, command.FlagSet)
|
||||
|
||||
return &command
|
||||
}
|
||||
|
||||
func (c *removeCommand) Execute() error {
|
||||
if c.fromResourceType == "" {
|
||||
return flagNotSetError{flagText: "remove-from"}
|
||||
}
|
||||
|
||||
funcMap := map[string]func(*client.Client) error{
|
||||
listResource: c.removeAccountsFromList,
|
||||
}
|
||||
|
||||
doFunc, ok := funcMap[c.fromResourceType]
|
||||
if !ok {
|
||||
return unsupportedResourceTypeError{resourceType: c.fromResourceType}
|
||||
}
|
||||
|
||||
gtsClient, err := client.NewClientFromConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create the GoToSocial client; %w", err)
|
||||
}
|
||||
|
||||
return doFunc(gtsClient)
|
||||
}
|
||||
|
||||
func (c *removeCommand) removeAccountsFromList(gtsClient *client.Client) error {
|
||||
if c.listID == "" {
|
||||
return flagNotSetError{flagText: listIDFlag}
|
||||
}
|
||||
|
||||
if len(c.accountIDs) == 0 {
|
||||
return noAccountIDsSpecifiedError{}
|
||||
}
|
||||
|
||||
if err := gtsClient.RemoveAccountsFromList(c.listID, []string(c.accountIDs)); err != nil {
|
||||
return fmt.Errorf("unable to remove the accounts from the list; %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Successfully removed the account(s) from the list.")
|
||||
|
||||
return nil
|
||||
}
|
|
@ -51,7 +51,7 @@ func (c *showCommand) Execute() error {
|
|||
accountResource: c.showAccount,
|
||||
statusResource: c.showStatus,
|
||||
timelineResource: c.showTimeline,
|
||||
listResource: c.showLists,
|
||||
listResource: c.showList,
|
||||
}
|
||||
|
||||
doFunc, ok := funcMap[c.resourceType]
|
||||
|
@ -163,6 +163,34 @@ func (c *showCommand) showTimeline(gts *client.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *showCommand) showList(gts *client.Client) error {
|
||||
if c.listID == "" {
|
||||
return c.showLists(gts)
|
||||
}
|
||||
|
||||
list, err := gts.GetList(c.listID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve the list; %w", err)
|
||||
}
|
||||
|
||||
accounts, err := gts.GetAccountsFromList(c.listID, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve the accounts from the list; %w", err)
|
||||
}
|
||||
|
||||
if len(accounts) > 0 {
|
||||
accountMap := make(map[string]string)
|
||||
for i := range accounts {
|
||||
accountMap[accounts[i].ID] = accounts[i].Username
|
||||
}
|
||||
list.Accounts = accountMap
|
||||
}
|
||||
|
||||
fmt.Println(list)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *showCommand) showLists(gts *client.Client) error {
|
||||
lists, err := gts.GetAllLists()
|
||||
if err != nil {
|
||||
|
@ -176,10 +204,7 @@ func (c *showCommand) showLists(gts *client.Client) error {
|
|||
}
|
||||
|
||||
fmt.Println(utilities.HeaderFormat("LISTS"))
|
||||
|
||||
for i := range lists {
|
||||
fmt.Printf("\n%s\n", lists[i])
|
||||
}
|
||||
fmt.Println(lists)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ func (c *updateCommand) updateList(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
fmt.Println("Successfully updated the list.")
|
||||
fmt.Printf("\n%s\n", updatedList)
|
||||
fmt.Println(updatedList)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ const (
|
|||
listPath string = "/api/v1/lists"
|
||||
)
|
||||
|
||||
func (g *Client) GetAllLists() ([]model.List, error) {
|
||||
func (g *Client) GetAllLists() (model.Lists, error) {
|
||||
url := g.Authentication.Instance + listPath
|
||||
|
||||
var lists []model.List
|
||||
|
@ -44,7 +44,7 @@ func (g *Client) GetList(listID string) (model.List, error) {
|
|||
}
|
||||
|
||||
func (g *Client) CreateList(title string, repliesPolicy model.ListRepliesPolicy) (model.List, error) {
|
||||
params := struct {
|
||||
form := struct {
|
||||
Title string `json:"title"`
|
||||
RepliesPolicy model.ListRepliesPolicy `json:"replies_policy"`
|
||||
}{
|
||||
|
@ -52,9 +52,9 @@ func (g *Client) CreateList(title string, repliesPolicy model.ListRepliesPolicy)
|
|||
RepliesPolicy: repliesPolicy,
|
||||
}
|
||||
|
||||
data, err := json.Marshal(params)
|
||||
data, err := json.Marshal(form)
|
||||
if err != nil {
|
||||
return model.List{}, fmt.Errorf("unable to marshal the request body; %w", err)
|
||||
return model.List{}, fmt.Errorf("unable to marshal the form; %w", err)
|
||||
}
|
||||
|
||||
requestBody := bytes.NewBuffer(data)
|
||||
|
@ -73,7 +73,7 @@ func (g *Client) CreateList(title string, repliesPolicy model.ListRepliesPolicy)
|
|||
}
|
||||
|
||||
func (g *Client) UpdateList(listToUpdate model.List) (model.List, error) {
|
||||
params := struct {
|
||||
form := struct {
|
||||
Title string `json:"title"`
|
||||
RepliesPolicy model.ListRepliesPolicy `json:"replies_policy"`
|
||||
}{
|
||||
|
@ -81,9 +81,9 @@ func (g *Client) UpdateList(listToUpdate model.List) (model.List, error) {
|
|||
RepliesPolicy: listToUpdate.RepliesPolicy,
|
||||
}
|
||||
|
||||
data, err := json.Marshal(params)
|
||||
data, err := json.Marshal(form)
|
||||
if err != nil {
|
||||
return model.List{}, fmt.Errorf("unable to marshal the request body; %w", err)
|
||||
return model.List{}, fmt.Errorf("unable to marshal the form; %w", err)
|
||||
}
|
||||
|
||||
requestBody := bytes.NewBuffer(data)
|
||||
|
@ -106,3 +106,69 @@ func (g *Client) DeleteList(listID string) error {
|
|||
|
||||
return g.sendRequest(http.MethodDelete, url, nil, nil)
|
||||
}
|
||||
|
||||
func (g *Client) AddAccountsToList(listID string, accountIDs []string) error {
|
||||
form := struct {
|
||||
AccountIDs []string `json:"account_ids"`
|
||||
}{
|
||||
AccountIDs: accountIDs,
|
||||
}
|
||||
|
||||
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 + listPath + "/" + listID + "/accounts"
|
||||
|
||||
if err := g.sendRequest(http.MethodPost, url, requestBody, nil); err != nil {
|
||||
return fmt.Errorf(
|
||||
"received an error after sending the request to add the accounts to the list; %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Client) RemoveAccountsFromList(listID string, accountIDs []string) error {
|
||||
form := struct {
|
||||
AccountIDs []string `json:"account_ids"`
|
||||
}{
|
||||
AccountIDs: accountIDs,
|
||||
}
|
||||
|
||||
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 + listPath + "/" + listID + "/accounts"
|
||||
|
||||
if err := g.sendRequest(http.MethodDelete, url, requestBody, nil); err != nil {
|
||||
return fmt.Errorf(
|
||||
"received an error after sending the request to remove the accounts from the list; %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Client) GetAccountsFromList(listID string, limit int) ([]model.Account, error) {
|
||||
path := fmt.Sprintf("%s/%s/accounts?limit=%d", listPath, listID, limit)
|
||||
url := g.Authentication.Instance + path
|
||||
|
||||
var accounts []model.Account
|
||||
|
||||
if err := g.sendRequest(http.MethodGet, url, nil, &accounts); err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"received an error after sending the request to get the accounts from the list; %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return accounts, nil
|
||||
}
|
||||
|
|
|
@ -73,17 +73,57 @@ type List struct {
|
|||
ID string `json:"id"`
|
||||
RepliesPolicy ListRepliesPolicy `json:"replies_policy"`
|
||||
Title string `json:"title"`
|
||||
Accounts map[string]string
|
||||
}
|
||||
|
||||
func (l List) String() string {
|
||||
format := `%s %s
|
||||
%s %s
|
||||
%s %s`
|
||||
format := `
|
||||
%s
|
||||
%s
|
||||
|
||||
return fmt.Sprintf(
|
||||
%s
|
||||
%s
|
||||
|
||||
%s
|
||||
%s
|
||||
|
||||
%s`
|
||||
|
||||
output := fmt.Sprintf(
|
||||
format,
|
||||
utilities.FieldFormat("List ID:"), l.ID,
|
||||
utilities.FieldFormat("Title:"), l.Title,
|
||||
utilities.FieldFormat("Replies Policy:"), l.RepliesPolicy,
|
||||
utilities.HeaderFormat("LIST TITLE:"), l.Title,
|
||||
utilities.HeaderFormat("LIST ID:"), l.ID,
|
||||
utilities.HeaderFormat("REPLIES POLICY:"), l.RepliesPolicy,
|
||||
utilities.HeaderFormat("ADDED ACCOUNTS:"),
|
||||
)
|
||||
|
||||
if len(l.Accounts) > 0 {
|
||||
for id, name := range l.Accounts {
|
||||
output += fmt.Sprintf(
|
||||
"\n • %s (%s)",
|
||||
utilities.DisplayNameFormat(name),
|
||||
id,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
output += "\n None"
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
type Lists []List
|
||||
|
||||
func (l Lists) String() string {
|
||||
output := ""
|
||||
|
||||
for i := range l {
|
||||
output += fmt.Sprintf(
|
||||
"\n%s (%s)",
|
||||
l[i].Title,
|
||||
l[i].ID,
|
||||
)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue