func (o *OAuthSession) Frontpage(sort PopularitySort, params ListingOptions) ([]*Submission, error) {
	return o.SubredditSubmissions("", sort, params)
}
Frontpage returns the submissions on the default reddit frontpage using OAuth.
package geddit
import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
	"github.com/beefsack/go-rate"
	"github.com/google/go-querystring/query"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)
type transport struct {
	http.RoundTripper
	useragent string
}
func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	req.Header.Set("User-Agent", t.useragent)
	return t.RoundTripper.RoundTrip(req)
}
type OAuthSession struct {
	Client       *http.Client
	ClientID     string
	ClientSecret string
	OAuthConfig  *oauth2.Config
	UserAgent string
	ctx       context.Context
	throttle  *rate.RateLimiter
}
func NewOAuthSession(clientID, clientSecret, useragent, redirectURL string) (*OAuthSession, error) {
	o := &OAuthSession{}
	if len(useragent) > 0 {
		o.UserAgent = useragent
	} else {
		o.UserAgent = "Geddit Reddit Bot https://github.com/jzelinskie/geddit"
	}
	o.OAuthConfig = &oauth2.Config{
		ClientID:     clientID,
		ClientSecret: clientSecret,
		Endpoint: oauth2.Endpoint{
			AuthURL:  "https://www.reddit.com/api/v1/authorize",
			TokenURL: "https://www.reddit.com/api/v1/access_token",
		},
		RedirectURL: redirectURL,
	}
	c := &http.Client{}
	c.Transport = &transport{http.DefaultTransport, o.UserAgent}
	o.ctx = context.WithValue(context.Background(), oauth2.HTTPClient, c)
	return o, nil
}
func (o *OAuthSession) Throttle(interval time.Duration) {
	if interval == 0 {
		o.throttle = nil
		return
	}
	o.throttle = rate.New(1, interval)
}
func (o *OAuthSession) LoginAuth(username, password string) error {
	t, err := o.OAuthConfig.PasswordCredentialsToken(o.ctx, username, password)
	if err != nil {
		return err
	}
	if !t.Valid() {
		msg := "Invalid OAuth token"
		if t != nil {
			if extra := t.Extra("error"); extra != nil {
				msg = fmt.Sprintf("%s: %s", msg, extra)
			}
		}
		return errors.New(msg)
	}
	o.Client = o.OAuthConfig.Client(o.ctx, t)
	return nil
}
func (o *OAuthSession) AuthCodeURL(state string, scopes []string) string {
	o.OAuthConfig.Scopes = scopes
	return o.OAuthConfig.AuthCodeURL(state, oauth2.AccessTypeOnline)
}
func (o *OAuthSession) CodeAuth(code string) error {
	t, err := o.OAuthConfig.Exchange(o.ctx, code)
	if err != nil {
		return err
	}
	o.Client = o.OAuthConfig.Client(o.ctx, t)
	return nil
}
func (o *OAuthSession) NeedsCaptcha() (bool, error) {
	var b bool
	err := o.getBody("https://oauth.reddit.com/api/needs_captcha", &b)
	if err != nil {
		return false, err
	}
	return b, nil
}
func (o *OAuthSession) NewCaptcha() (string, error) {
	v := url.Values{
		"api_type": {"json"},
	}
	type captcha struct {
		Json struct {
			Errors [][]string
			Data   struct {
				Iden string
			}
		}
	}
	c := &captcha{}
	err := o.postBody("https://oauth.reddit.com/api/new_captcha", v, c)
	if err != nil {
		return "", err
	}
	return c.Json.Data.Iden, nil
}
func (o *OAuthSession) getBody(link string, d interface{}) error {
	req, err := http.NewRequest("GET", link, nil)
	if err != nil {
		return err
	}
	if o.Client == nil {
		return errors.New("OAuth Session lacks HTTP client! Use func (o OAuthSession) LoginAuth() to make one.")
	}
	if o.throttle != nil {
		o.throttle.Wait()
	}
	resp, err := o.Client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(body, d); err != nil {
		return err
	}
	return nil
}
func (o *OAuthSession) Me() (*Redditor, error) {
	r := &Redditor{}
	err := o.getBody("https://oauth.reddit.com/api/v1/me", r)
	if err != nil {
		return nil, err
	}
	return r, nil
}
func (o *OAuthSession) MyKarma() ([]Karma, error) {
	type karma struct {
		Data []Karma
	}
	k := &karma{}
	err := o.getBody("https://oauth.reddit.com/api/v1/me/karma", k)
	if err != nil {
		return nil, err
	}
	return k.Data, nil
}
func (o *OAuthSession) MyPreferences() (*Preferences, error) {
	p := &Preferences{}
	err := o.getBody("https://oauth.reddit.com/api/v1/me/prefs", p)
	if err != nil {
		return nil, err
	}
	return p, nil
}
func (o *OAuthSession) MyFriends() ([]Friend, error) {
	type friends struct {
		Data struct {
			Children []Friend
		}
	}
	f := &friends{}
	err := o.getBody("https://oauth.reddit.com/api/v1/me/friends", f)
	if err != nil {
		return nil, err
	}
	return f.Data.Children, nil
}
func (o *OAuthSession) MyTrophies() ([]*Trophy, error) {
	type trophyData struct {
		Data struct {
			Trophies []struct {
				Data Trophy
			}
		}
	}
	t := &trophyData{}
	err := o.getBody("https://oauth.reddit.com/api/v1/me/trophies", t)
	if err != nil {
		return nil, err
	}
	var myTrophies []*Trophy
	for _, trophy := range t.Data.Trophies {
		myTrophies = append(myTrophies, &trophy.Data)
	}
	return myTrophies, nil
}
func (o *OAuthSession) Listing(username, listing string, sort PopularitySort, params ListingOptions) ([]*Submission, error) {
	p, err := query.Values(params)
	if err != nil {
		return nil, err
	}
	if sort != "" {
		p.Set("sort", string(sort))
	}
	type resp struct {
		Data struct {
			Children []struct {
				Data *Submission
			}
		}
	}
	r := &resp{}
	url := fmt.Sprintf("https://oauth.reddit.com/user/%s/%s?%s", username, listing, p.Encode())
	err = o.getBody(url, r)
	if err != nil {
		return nil, err
	}
	submissions := make([]*Submission, len(r.Data.Children))
	for i, child := range r.Data.Children {
		submissions[i] = child.Data
	}
	return submissions, nil
}
func (o *OAuthSession) Upvoted(username string, sort PopularitySort, params ListingOptions) ([]*Submission, error) {
	return o.Listing(username, "upvoted", sort, params)
}
func (o *OAuthSession) MyUpvoted(sort PopularitySort, params ListingOptions) ([]*Submission, error) {
	me, err := o.Me()
	if err != nil {
		return nil, err
	}
	return o.Listing(me.Name, "upvoted", sort, params)
}
func (o *OAuthSession) AboutRedditor(user string) (*Redditor, error) {
	type redditor struct {
		Data Redditor
	}
	r := &redditor{}
	link := fmt.Sprintf("https://oauth.reddit.com/user/%s/about", user)
	err := o.getBody(link, r)
	if err != nil {
		return nil, err
	}
	return &r.Data, nil
}
func (o *OAuthSession) UserTrophies(user string) ([]*Trophy, error) {
	type trophyData struct {
		Data struct {
			Trophies []struct {
				Data Trophy
			}
		}
	}
	t := &trophyData{}
	url := fmt.Sprintf("https://oauth.reddit.com/api/v1/user/%s/trophies", user)
	err := o.getBody(url, t)
	if err != nil {
		return nil, err
	}
	var trophies []*Trophy
	for _, trophy := range t.Data.Trophies {
		trophies = append(trophies, &trophy.Data)
	}
	return trophies, nil
}
func (o *OAuthSession) AboutSubreddit(name string) (*Subreddit, error) {
	type subreddit struct {
		Data Subreddit
	}
	sr := &subreddit{}
	link := fmt.Sprintf("https://oauth.reddit.com/r/%s/about", name)
	err := o.getBody(link, sr)
	if err != nil {
		return nil, err
	}
	return &sr.Data, nil
}
func (o *OAuthSession) Comments(h *Submission, sort PopularitySort, params ListingOptions) ([]*Comment, error) {
	p, err := query.Values(params)
	if err != nil {
		return nil, err
	}
	if sort != "" {
		p.Set("sort", string(sort))
	}
	var c interface{}
	link := fmt.Sprintf("https://oauth.reddit.com/comments/%s?%s", h.ID, p.Encode())
	err = o.getBody(link, &c)
	if err != nil {
		return nil, err
	}
	helper := new(helper)
	helper.buildComments(c)
	return helper.comments, nil
}
func (o *OAuthSession) postBody(link string, form url.Values, d interface{}) error {
	req, err := http.NewRequest("POST", link, strings.NewReader(form.Encode()))
	if err != nil {
		return err
	}
	req.PostForm = form
	if o.Client == nil {
		return errors.New("OAuth Session lacks HTTP client! Use func (o OAuthSession) LoginAuth() to make one.")
	}
	if o.throttle != nil {
		o.throttle.Wait()
	}
	resp, err := o.Client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if d != nil {
		err = json.Unmarshal(body, d)
		if err != nil {
			return err
		}
	}
	return nil
}
func (o *OAuthSession) Submit(ns *NewSubmission) (*Submission, error) {
	v := url.Values{
		"title":       {ns.Title},
		"url":         {ns.Content},
		"text":        {ns.Content},
		"sr":          {ns.Subreddit},
		"sendreplies": {strconv.FormatBool(ns.SendReplies)},
		"resubmit":    {strconv.FormatBool(ns.Resubmit)},
		"api_type":    {"json"},
	}
	if ns.Self {
		v.Add("kind", "self")
	} else {
		v.Add("kind", "link")
	}
	type submission struct {
		Json struct {
			Errors [][]string
			Data   Submission
		}
	}
	submit := &submission{}
	err := o.postBody("https://oauth.reddit.com/api/submit", v, submit)
	if err != nil {
		return nil, err
	}
	return &submit.Json.Data, nil
}
func (o *OAuthSession) Delete(d Deleter) error {
	v := url.Values{}
	v.Add("id", d.deleteID())
	return o.postBody("https://oauth.reddit.com/api/del", v, nil)
}
func (o *OAuthSession) SubredditSubmissions(subreddit string, sort PopularitySort, params ListingOptions) ([]*Submission, error) {
	v, err := query.Values(params)
	if err != nil {
		return nil, err
	}
	baseUrl := "https://oauth.reddit.com"
	if subreddit != "" {
		baseUrl += "/r/" + subreddit
	}
	redditURL := fmt.Sprintf(baseUrl+"/%s.json?%s", sort, v.Encode())
	type Response struct {
		Data struct {
			Children []struct {
				Data *Submission
			}
		}
	}
	r := new(Response)
	err = o.getBody(redditURL, r)
	if err != nil {
		return nil, err
	}
	submissions := make([]*Submission, len(r.Data.Children))
	for i, child := range r.Data.Children {
		submissions[i] = child.Data
	}
	return submissions, nil
}