inital commit
This commit is contained in:
34
actions.go
34
actions.go
@@ -20,20 +20,20 @@ import (
|
||||
// on the "+Fav" anchor on the submission page. We fetch the submission to
|
||||
// scrape the key, then follow the link. The whole exchange happens
|
||||
// through the rate-limited transport.
|
||||
func (c *Client) Fav(ctx context.Context, id SubmissionID) error {
|
||||
return c.toggleFavorite(ctx, id, true)
|
||||
func (c *Client) Fav(ctx context.Context, id SubmissionID, opts ...Option) error {
|
||||
return c.toggleFavorite(ctx, id, true, opts)
|
||||
}
|
||||
|
||||
// Unfav removes a submission from the logged-in user's favorites.
|
||||
// Idempotent: if not favorited, no-op.
|
||||
func (c *Client) Unfav(ctx context.Context, id SubmissionID) error {
|
||||
return c.toggleFavorite(ctx, id, false)
|
||||
func (c *Client) Unfav(ctx context.Context, id SubmissionID, opts ...Option) error {
|
||||
return c.toggleFavorite(ctx, id, false, opts)
|
||||
}
|
||||
|
||||
// toggleFavorite implements both Fav and Unfav: scrape the per-state link
|
||||
// off the submission page and follow it. wantFav=true means "fav if not
|
||||
// already faved"; wantFav=false means "unfav if currently faved".
|
||||
func (c *Client) toggleFavorite(ctx context.Context, id SubmissionID, wantFav bool) error {
|
||||
func (c *Client) toggleFavorite(ctx context.Context, id SubmissionID, wantFav bool, reqOpts []Option) error {
|
||||
if id <= 0 {
|
||||
return fmt.Errorf("fa: toggleFavorite: id must be > 0")
|
||||
}
|
||||
@@ -54,30 +54,30 @@ func (c *Client) toggleFavorite(ctx context.Context, id SubmissionID, wantFav bo
|
||||
return fmt.Errorf("%w: submission %d: no fav/unfav link on page (not logged in?)", ErrUnauthorized, id)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}, reqOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if alreadyInDesiredState {
|
||||
return nil
|
||||
}
|
||||
return c.followAction(ctx, actionURL)
|
||||
return c.followAction(ctx, actionURL, reqOpts...)
|
||||
}
|
||||
|
||||
// Watch starts watching a user. Idempotent: if already watching, no-op.
|
||||
// The key + endpoint live on a button on the user's profile (or any page
|
||||
// that user is the "owner" of, like a journal). We scrape the profile.
|
||||
func (c *Client) Watch(ctx context.Context, name string) error {
|
||||
return c.toggleWatch(ctx, name, true)
|
||||
func (c *Client) Watch(ctx context.Context, name string, opts ...Option) error {
|
||||
return c.toggleWatch(ctx, name, true, opts)
|
||||
}
|
||||
|
||||
// Unwatch stops watching a user. Idempotent.
|
||||
func (c *Client) Unwatch(ctx context.Context, name string) error {
|
||||
return c.toggleWatch(ctx, name, false)
|
||||
func (c *Client) Unwatch(ctx context.Context, name string, opts ...Option) error {
|
||||
return c.toggleWatch(ctx, name, false, opts)
|
||||
}
|
||||
|
||||
// toggleWatch fetches the user page, picks watch or unwatch link by state.
|
||||
func (c *Client) toggleWatch(ctx context.Context, name string, wantWatch bool) error {
|
||||
func (c *Client) toggleWatch(ctx context.Context, name string, wantWatch bool, reqOpts []Option) error {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return fmt.Errorf("fa: toggleWatch: empty name")
|
||||
@@ -99,14 +99,14 @@ func (c *Client) toggleWatch(ctx context.Context, name string, wantWatch bool) e
|
||||
return fmt.Errorf("%w: user %q: no watch/unwatch link on page", ErrUnauthorized, name)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}, reqOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if alreadyInDesiredState {
|
||||
return nil
|
||||
}
|
||||
return c.followAction(ctx, actionURL)
|
||||
return c.followAction(ctx, actionURL, reqOpts...)
|
||||
}
|
||||
|
||||
// findFavLinks scans a submission view page for the "+Fav" and "−Fav"
|
||||
@@ -155,10 +155,11 @@ func findWatchLinks(doc *goquery.Document, name string) (watchURL, unwatchURL st
|
||||
// the transport+classifier; 5xx becomes HTTPError. We don't parse the
|
||||
// response body FA's success states are too varied to verify reliably
|
||||
// from HTML alone.
|
||||
func (c *Client) followAction(ctx context.Context, actionURL string) error {
|
||||
func (c *Client) followAction(ctx context.Context, actionURL string, opts ...Option) error {
|
||||
if actionURL == "" {
|
||||
return fmt.Errorf("fa: followAction: empty URL")
|
||||
}
|
||||
ctx = c.applyRequestOptions(ctx, opts)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, actionURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -192,7 +193,8 @@ func (c *Client) followAction(ctx context.Context, actionURL string) error {
|
||||
//
|
||||
// Returns the response body for callers that need to parse a confirmation
|
||||
// (e.g., to extract a newly-posted comment's ID).
|
||||
func (c *Client) postForm(ctx context.Context, rawURL string, form url.Values) ([]byte, error) {
|
||||
func (c *Client) postForm(ctx context.Context, rawURL string, form url.Values, opts ...Option) ([]byte, error) {
|
||||
ctx = c.applyRequestOptions(ctx, opts)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, rawURL, strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user