This repository has been archived on 2022-03-09. You can view files and clone it, but cannot push or open issues or pull requests.
snoo2nebby/vendor/github.com/turnage/graw/reddit/reaper.go

157 lines
3.1 KiB
Go

package reddit
import (
"net/http"
"net/url"
"strings"
"sync"
"time"
)
var (
// scheme is a map of TLS=[true|false] to the scheme for that setting.
scheme = map[bool]string{
true: "https",
false: "http",
}
formEncoding = map[string][]string{
"content-type": {"application/x-www-form-urlencoded"},
}
)
type reaperConfig struct {
client client
parser parser
hostname string
reapSuffix string
tls bool
rate time.Duration
}
// reaper is a high level api for Reddit HTTP requests.
type reaper interface {
// reap executes a GET request to Reddit and returns the elements from
// the endpoint.
reap(path string, values map[string]string) (Harvest, error)
// sow executes a POST request to Reddit.
sow(path string, values map[string]string) error
// get_sow executes a POST request to Reddit
// and returns the response, usually the posted item
get_sow(path string, values map[string]string) (Submission, error)
}
type reaperImpl struct {
cli client
parser parser
hostname string
reapSuffix string
scheme string
rate time.Duration
last time.Time
mu *sync.Mutex
}
func newReaper(c reaperConfig) reaper {
return &reaperImpl{
cli: c.client,
parser: c.parser,
hostname: c.hostname,
reapSuffix: c.reapSuffix,
scheme: scheme[c.tls],
rate: c.rate,
mu: &sync.Mutex{},
}
}
func (r *reaperImpl) reap(path string, values map[string]string) (Harvest, error) {
r.rateBlock()
resp, err := r.cli.Do(
&http.Request{
Method: "GET",
URL: r.url(r.path(path, r.reapSuffix), values),
Host: r.hostname,
},
)
if err != nil {
return Harvest{}, err
}
comments, posts, messages, mores, err := r.parser.parse(resp)
return Harvest{
Comments: comments,
Posts: posts,
Messages: messages,
Mores: mores,
}, err
}
func (r *reaperImpl) sow(path string, values map[string]string) error {
r.rateBlock()
_, err := r.cli.Do(
&http.Request{
Method: "POST",
Header: formEncoding,
Host: r.hostname,
URL: r.url(path, values),
},
)
return err
}
func (r *reaperImpl) get_sow(path string, values map[string]string) (Submission, error) {
r.rateBlock()
values["api_type"] = "json"
resp, err := r.cli.Do(
&http.Request{
Method: "POST",
Header: formEncoding,
Host: r.hostname,
URL: r.url(path, values),
},
)
if err != nil {
return Submission{}, err
}
return r.parser.parse_submitted(resp)
}
func (r *reaperImpl) rateBlock() {
r.mu.Lock()
defer r.mu.Unlock()
if time.Since(r.last) < r.rate {
<-time.After(r.last.Add(r.rate).Sub(time.Now()))
}
r.last = time.Now()
}
func (r *reaperImpl) url(path string, values map[string]string) *url.URL {
return &url.URL{
Scheme: r.scheme,
Host: r.hostname,
Path: path,
RawQuery: r.formatValues(values).Encode(),
}
}
func (r *reaperImpl) path(p string, suff string) string {
if strings.HasSuffix(p, suff) {
return p
}
return p + suff
}
func (r *reaperImpl) formatValues(values map[string]string) url.Values {
formattedValues := url.Values{}
for key, value := range values {
formattedValues[key] = []string{value}
}
return formattedValues
}