feat: accept or reject follow requests

Add support to allow users to accept or reject follow requests.
This commit is contained in:
Dan Anglin 2024-06-10 10:58:43 +01:00
parent d21f1fbf6a
commit 5fb55ed2cf
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
6 changed files with 205 additions and 29 deletions

View file

@ -28,6 +28,8 @@ const (
commandUnfollow string = "unfollow" commandUnfollow string = "unfollow"
commandBlock string = "block" commandBlock string = "block"
commandUnblock string = "unblock" commandUnblock string = "unblock"
commandAccept string = "accept"
commandReject string = "reject"
) )
var ( var (
@ -60,6 +62,8 @@ func run() error {
commandUnfollow: "Unfollow a resource (e.g. an account)", commandUnfollow: "Unfollow a resource (e.g. an account)",
commandBlock: "Block a resource (e.g. an account)", commandBlock: "Block a resource (e.g. an account)",
commandUnblock: "Unblock a resource (e.g. an account)", commandUnblock: "Unblock a resource (e.g. an account)",
commandAccept: "Accept a request (e.g. a follow request)",
commandReject: "Reject a request (e.g. a follow request)",
} }
topLevelFlags := executor.TopLevelFlags{ topLevelFlags := executor.TopLevelFlags{
@ -108,6 +112,13 @@ func run() error {
var err error var err error
switch command { switch command {
case commandAccept:
exe := executor.NewAcceptOrRejectExecutor(
topLevelFlags,
commandAccept,
commandSummaries[commandAccept],
)
err = executor.Execute(exe, args)
case commandAdd: case commandAdd:
exe := executor.NewAddExecutor( exe := executor.NewAddExecutor(
topLevelFlags, topLevelFlags,
@ -159,6 +170,13 @@ func run() error {
commandSummaries[commandLogin], commandSummaries[commandLogin],
) )
err = executor.Execute(exe, args) err = executor.Execute(exe, args)
case commandReject:
exe := executor.NewAcceptOrRejectExecutor(
topLevelFlags,
commandReject,
commandSummaries[commandReject],
)
err = executor.Execute(exe, args)
case commandRemove: case commandRemove:
exe := executor.NewRemoveExecutor( exe := executor.NewRemoveExecutor(
topLevelFlags, topLevelFlags,
@ -174,10 +192,20 @@ func run() error {
) )
err = executor.Execute(exe, args) err = executor.Execute(exe, args)
case commandUnfollow: case commandUnfollow:
exe := executor.NewFollowExecutor(topLevelFlags, commandUnfollow, commandSummaries[commandUnfollow], true) exe := executor.NewFollowExecutor(
topLevelFlags,
commandUnfollow,
commandSummaries[commandUnfollow],
true,
)
err = executor.Execute(exe, args) err = executor.Execute(exe, args)
case commandUnblock: case commandUnblock:
exe := executor.NewBlockExecutor(topLevelFlags, commandUnblock, commandSummaries[commandUnblock], true) exe := executor.NewBlockExecutor(
topLevelFlags,
commandUnblock,
commandSummaries[commandUnblock],
true,
)
err = executor.Execute(exe, args) err = executor.Execute(exe, args)
case commandShow: case commandShow:
exe := executor.NewShowExecutor(topLevelFlags, commandShow, commandSummaries[commandShow]) exe := executor.NewShowExecutor(topLevelFlags, commandShow, commandSummaries[commandShow])

View file

@ -145,7 +145,7 @@ func (g *Client) UnblockAccount(accountID string) error {
func (g *Client) GetBlockedAccounts(limit int) (model.AccountList, error) { func (g *Client) GetBlockedAccounts(limit int) (model.AccountList, error) {
url := g.Authentication.Instance + fmt.Sprintf("/api/v1/blocks?limit=%d", limit) url := g.Authentication.Instance + fmt.Sprintf("/api/v1/blocks?limit=%d", limit)
accounts := make([]model.Account, limit) var accounts []model.Account
if err := g.sendRequest(http.MethodGet, url, nil, &accounts); err != nil { if err := g.sendRequest(http.MethodGet, url, nil, &accounts); err != nil {
return model.AccountList{}, fmt.Errorf("received an error after sending the request to get the list of blocked accounts: %w", err) return model.AccountList{}, fmt.Errorf("received an error after sending the request to get the list of blocked accounts: %w", err)
@ -180,3 +180,40 @@ func (g *Client) SetPrivateNote(accountID, note string) error {
return nil return nil
} }
func (g *Client) GetFollowRequests(limit int) (model.AccountList, error) {
url := g.Authentication.Instance + fmt.Sprintf("/api/v1/follow_requests?limit=%d", limit)
var accounts []model.Account
if err := g.sendRequest(http.MethodGet, url, nil, &accounts); err != nil {
return model.AccountList{}, fmt.Errorf("received an error after sending the request to get the list of follow requests: %w", err)
}
requests := model.AccountList{
Type: model.AccountListFollowRequests,
Accounts: accounts,
}
return requests, nil
}
func (g *Client) AcceptFollowRequest(accountID string) error {
url := g.Authentication.Instance + "/api/v1/follow_requests/" + accountID + "/authorize"
if err := g.sendRequest(http.MethodPost, url, nil, nil); err != nil {
return fmt.Errorf("received an error after sending the request to accept the follow request: %w", err)
}
return nil
}
func (g *Client) RejectFollowRequest(accountID string) error {
url := g.Authentication.Instance + "/api/v1/follow_requests/" + accountID + "/reject"
if err := g.sendRequest(http.MethodPost, url, nil, nil); err != nil {
return fmt.Errorf("received an error after sending the request to reject the follow request: %w", err)
}
return nil
}

View file

@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later
package executor
import (
"flag"
"fmt"
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
)
type AcceptOrRejectExecutor struct {
*flag.FlagSet
topLevelFlags TopLevelFlags
resourceType string
accountName string
action string
}
func NewAcceptOrRejectExecutor(tlf TopLevelFlags, name, summary string) *AcceptOrRejectExecutor {
acceptExe := AcceptOrRejectExecutor{
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
topLevelFlags: tlf,
action: name,
}
acceptExe.StringVar(&acceptExe.resourceType, flagType, "", "Specify the type of resource to accept or reject")
acceptExe.StringVar(&acceptExe.accountName, flagAccountName, "", "Specify the account name in full (username@domain)")
acceptExe.Usage = commandUsageFunc(name, summary, acceptExe.FlagSet)
return &acceptExe
}
func (a *AcceptOrRejectExecutor) Execute() error {
funcMap := map[string]func(*client.Client) error{
resourceFollowRequest: a.acceptOrRejectFollowRequest,
}
doFunc, ok := funcMap[a.resourceType]
if !ok {
return UnsupportedTypeError{resourceType: a.resourceType}
}
gtsClient, err := client.NewClientFromConfig(a.topLevelFlags.ConfigDir)
if err != nil {
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
}
return doFunc(gtsClient)
}
func (a *AcceptOrRejectExecutor) acceptOrRejectFollowRequest(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, false, a.accountName, a.topLevelFlags.ConfigDir)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}
switch a.action {
case "accept":
return a.acceptFollowRequest(gtsClient, accountID)
case "reject":
return a.rejectFollowRequest(gtsClient, accountID)
default:
return nil
}
}
func (a *AcceptOrRejectExecutor) acceptFollowRequest(gtsClient *client.Client, accountID string) error {
if err := gtsClient.AcceptFollowRequest(accountID); err != nil {
return fmt.Errorf("unable to accept the follow request: %w", err)
}
fmt.Println("Successfully accepted the follow request.")
return nil
}
func (a *AcceptOrRejectExecutor) rejectFollowRequest(gtsClient *client.Client, accountID string) error {
if err := gtsClient.RejectFollowRequest(accountID); err != nil {
return fmt.Errorf("unable to reject the follow request: %w", err)
}
fmt.Println("Successfully rejected the follow request.")
return nil
}

View file

@ -35,19 +35,20 @@ const (
flagType = "type" flagType = "type"
flagVisibility = "visibility" flagVisibility = "visibility"
resourceAccount = "account" resourceAccount = "account"
resourceBlocked = "blocked" resourceBlocked = "blocked"
resourceBookmarks = "bookmarks" resourceBookmarks = "bookmarks"
resourceBoost = "boost" resourceBoost = "boost"
resourceFollowers = "followers" resourceFollowers = "followers"
resourceFollowing = "following" resourceFollowing = "following"
resourceInstance = "instance" resourceFollowRequest = "follow-request"
resourceLike = "like" resourceInstance = "instance"
resourceLiked = "liked" resourceLike = "like"
resourceList = "list" resourceLiked = "liked"
resourceNote = "note" resourceList = "list"
resourceStatus = "status" resourceNote = "note"
resourceStar = "star" resourceStatus = "status"
resourceStarred = "starred" resourceStar = "star"
resourceTimeline = "timeline" resourceStarred = "starred"
resourceTimeline = "timeline"
) )

View file

@ -58,17 +58,18 @@ func (s *ShowExecutor) Execute() error {
} }
funcMap := map[string]func(*client.Client) error{ funcMap := map[string]func(*client.Client) error{
resourceInstance: s.showInstance, resourceInstance: s.showInstance,
resourceAccount: s.showAccount, resourceAccount: s.showAccount,
resourceStatus: s.showStatus, resourceStatus: s.showStatus,
resourceTimeline: s.showTimeline, resourceTimeline: s.showTimeline,
resourceList: s.showList, resourceList: s.showList,
resourceFollowers: s.showFollowers, resourceFollowers: s.showFollowers,
resourceFollowing: s.showFollowing, resourceFollowing: s.showFollowing,
resourceBlocked: s.showBlocked, resourceBlocked: s.showBlocked,
resourceBookmarks: s.showBookmarks, resourceBookmarks: s.showBookmarks,
resourceLiked: s.showLiked, resourceLiked: s.showLiked,
resourceStarred: s.showLiked, resourceStarred: s.showLiked,
resourceFollowRequest: s.showFollowRequests,
} }
doFunc, ok := funcMap[s.resourceType] doFunc, ok := funcMap[s.resourceType]
@ -346,3 +347,18 @@ func (s *ShowExecutor) showLiked(gtsClient *client.Client) error {
return nil return nil
} }
func (s *ShowExecutor) showFollowRequests(gtsClient *client.Client) error {
accounts, err := gtsClient.GetFollowRequests(s.limit)
if err != nil {
return fmt.Errorf("unable to retrieve the list of follow requests: %w", err)
}
if len(accounts.Accounts) > 0 {
utilities.Display(accounts, *s.topLevelFlags.NoColor, s.topLevelFlags.Pager)
} else {
fmt.Println("You have no follow requests.")
}
return nil
}

View file

@ -186,6 +186,7 @@ const (
AccountListFollowers AccountListType = iota AccountListFollowers AccountListType = iota
AccountListFollowing AccountListFollowing
AccountListBlockedAccount AccountListBlockedAccount
AccountListFollowRequests
) )
type AccountList struct { type AccountList struct {
@ -203,6 +204,8 @@ func (a AccountList) Display(noColor bool) string {
output += utilities.HeaderFormat(noColor, "FOLLOWING:") output += utilities.HeaderFormat(noColor, "FOLLOWING:")
case AccountListBlockedAccount: case AccountListBlockedAccount:
output += utilities.HeaderFormat(noColor, "BLOCKED ACCOUNTS:") output += utilities.HeaderFormat(noColor, "BLOCKED ACCOUNTS:")
case AccountListFollowRequests:
output += utilities.HeaderFormat(noColor, "ACCOUNTS THAT HAVE REQUESTED TO FOLLOW YOU:")
default: default:
output += utilities.HeaderFormat(noColor, "ACCOUNTS:") output += utilities.HeaderFormat(noColor, "ACCOUNTS:")
} }