2020-05-04 20:51:16 -04:00
|
|
|
package geddit
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2020-05-16 21:46:16 -04:00
|
|
|
"strings"
|
2020-05-04 20:51:16 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// SearchService handles communication with the search
|
|
|
|
// related methods of the Reddit API
|
|
|
|
// IMPORTANT: for searches to include NSFW results, the
|
|
|
|
// user must check the following in their preferences:
|
|
|
|
// "include not safe for work (NSFW) search results in searches"
|
|
|
|
type SearchService interface {
|
2020-05-16 21:46:16 -04:00
|
|
|
Posts(query string) *PostSearchBuilder
|
|
|
|
Subreddits(query string) *SubredditSearchBuilder
|
|
|
|
Users(query string) *UserSearchBuilder
|
2020-05-04 20:51:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// SearchServiceOp implements the VoteService interface
|
|
|
|
type SearchServiceOp struct {
|
|
|
|
client *Client
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ SearchService = &SearchServiceOp{}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Posts searches for posts.
|
|
|
|
// By default, it searches for the most relevant posts of all time.
|
|
|
|
// To change the sorting, use PostSearchBuilder.Sort().
|
|
|
|
// Possible sort options: relevance, hot, top, new, comments.
|
|
|
|
// To change the timespan, use PostSearchBuilder.Timespan().
|
|
|
|
// Possible timespan options: hour, day, week, month, year, all.
|
|
|
|
func (s *SearchServiceOp) Posts(query string) *PostSearchBuilder {
|
|
|
|
b := new(PostSearchBuilder)
|
|
|
|
b.client = s.client
|
|
|
|
b.opts.Query = query
|
|
|
|
b.opts.Type = "link"
|
2020-05-16 22:34:01 -04:00
|
|
|
return b.Sort(SortRelevance).Timespan(TimespanAll)
|
2020-05-04 20:51:16 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Subreddits searches for subreddits.
|
|
|
|
func (s *SearchServiceOp) Subreddits(query string) *SubredditSearchBuilder {
|
|
|
|
b := new(SubredditSearchBuilder)
|
|
|
|
b.client = s.client
|
|
|
|
b.opts.Query = query
|
|
|
|
b.opts.Type = "sr"
|
|
|
|
return b
|
2020-05-05 08:49:33 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Users searches for users.
|
|
|
|
func (s *SearchServiceOp) Users(query string) *UserSearchBuilder {
|
|
|
|
b := new(UserSearchBuilder)
|
|
|
|
b.client = s.client
|
|
|
|
b.opts.Query = query
|
|
|
|
b.opts.Type = "user"
|
|
|
|
return b
|
2020-05-05 08:49:33 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
type searchOpts struct {
|
|
|
|
Query string `url:"q"`
|
|
|
|
Type string `url:"type,omitempty"`
|
|
|
|
After string `url:"after,omitempty"`
|
|
|
|
Before string `url:"before,omitempty"`
|
|
|
|
Limit int `url:"limit,omitempty"`
|
|
|
|
RestrictSubreddits bool `url:"restrict_sr,omitempty"`
|
|
|
|
Sort string `url:"sort,omitempty"`
|
|
|
|
Timespan string `url:"t,omitempty"`
|
2020-05-05 08:49:33 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// PostSearchBuilder helps conducts searches that return posts.
|
|
|
|
type PostSearchBuilder struct {
|
|
|
|
client *Client
|
|
|
|
subreddits []string
|
|
|
|
opts searchOpts
|
2020-05-04 20:51:16 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// After sets the after option.
|
|
|
|
func (b *PostSearchBuilder) After(after string) *PostSearchBuilder {
|
|
|
|
b.opts.After = after
|
|
|
|
return b
|
2020-05-04 20:51:16 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Before sets the before option.
|
|
|
|
func (b *PostSearchBuilder) Before(before string) *PostSearchBuilder {
|
|
|
|
b.opts.Before = before
|
|
|
|
return b
|
|
|
|
}
|
2020-05-04 20:51:16 -04:00
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Limit sets the limit option.
|
|
|
|
func (b *PostSearchBuilder) Limit(limit int) *PostSearchBuilder {
|
|
|
|
b.opts.Limit = limit
|
|
|
|
return b
|
2020-05-04 20:51:16 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 22:34:01 -04:00
|
|
|
// FromSubreddits restricts the search to happen in the specified subreddits only.
|
|
|
|
func (b *PostSearchBuilder) FromSubreddits(subreddits ...string) *PostSearchBuilder {
|
2020-05-16 21:46:16 -04:00
|
|
|
b.subreddits = subreddits
|
|
|
|
b.opts.RestrictSubreddits = len(subreddits) > 0
|
|
|
|
return b
|
|
|
|
}
|
2020-05-04 20:51:16 -04:00
|
|
|
|
2020-05-16 22:34:01 -04:00
|
|
|
// FromAll runs the search against r/all.
|
|
|
|
func (b *PostSearchBuilder) FromAll() *PostSearchBuilder {
|
|
|
|
return b.FromSubreddits()
|
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Sort sets the sort option.
|
|
|
|
func (b *PostSearchBuilder) Sort(sort Sort) *PostSearchBuilder {
|
|
|
|
b.opts.Sort = sort.String()
|
|
|
|
return b
|
|
|
|
}
|
2020-05-04 20:51:16 -04:00
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Timespan sets the timespan option.
|
|
|
|
func (b *PostSearchBuilder) Timespan(timespan Timespan) *PostSearchBuilder {
|
|
|
|
b.opts.Timespan = timespan.String()
|
|
|
|
return b
|
2020-05-04 20:51:16 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Do conducts the search.
|
|
|
|
func (b *PostSearchBuilder) Do(ctx context.Context) (*Links, *Response, error) {
|
|
|
|
path := "search"
|
|
|
|
if len(b.subreddits) > 0 {
|
|
|
|
path = fmt.Sprintf("r/%s/search", strings.Join(b.subreddits, "+"))
|
|
|
|
}
|
2020-05-04 20:51:16 -04:00
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
path, err := addOptions(path, b.opts)
|
2020-05-04 20:51:16 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
req, err := b.client.NewRequest(http.MethodGet, path, nil)
|
2020-05-04 20:51:16 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
root := new(rootListing)
|
|
|
|
resp, err := b.client.Do(ctx, req, root)
|
2020-05-04 20:51:16 -04:00
|
|
|
if err != nil {
|
2020-05-16 21:46:16 -04:00
|
|
|
return nil, resp, err
|
2020-05-04 20:51:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return root.getLinks(), resp, nil
|
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// SubredditSearchBuilder helps conducts searches that return subreddits.
|
|
|
|
type SubredditSearchBuilder struct {
|
|
|
|
client *Client
|
|
|
|
opts searchOpts
|
|
|
|
}
|
2020-05-04 20:51:16 -04:00
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// After sets the after option.
|
|
|
|
func (b *SubredditSearchBuilder) After(after string) *SubredditSearchBuilder {
|
|
|
|
b.opts.After = after
|
|
|
|
return b
|
|
|
|
}
|
2020-05-04 20:51:16 -04:00
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Before sets the before option.
|
|
|
|
func (b *SubredditSearchBuilder) Before(before string) *SubredditSearchBuilder {
|
|
|
|
b.opts.Before = before
|
|
|
|
return b
|
2020-05-04 20:51:16 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Limit sets the limit option.
|
|
|
|
func (b *SubredditSearchBuilder) Limit(limit int) *SubredditSearchBuilder {
|
|
|
|
b.opts.Limit = limit
|
|
|
|
return b
|
|
|
|
}
|
2020-05-04 20:51:16 -04:00
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Do conducts the search.
|
|
|
|
func (b *SubredditSearchBuilder) Do(ctx context.Context) (*Subreddits, *Response, error) {
|
|
|
|
path := "search"
|
|
|
|
path, err := addOptions(path, b.opts)
|
2020-05-04 20:51:16 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
req, err := b.client.NewRequest(http.MethodGet, path, nil)
|
2020-05-04 20:51:16 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
root := new(rootListing)
|
|
|
|
resp, err := b.client.Do(ctx, req, root)
|
2020-05-04 20:51:16 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return root.getSubreddits(), resp, nil
|
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// UserSearchBuilder helps conducts searches that return posts.
|
|
|
|
type UserSearchBuilder struct {
|
|
|
|
client *Client
|
|
|
|
opts searchOpts
|
2020-05-05 08:49:33 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// After sets the after option.
|
|
|
|
func (b *UserSearchBuilder) After(after string) *UserSearchBuilder {
|
|
|
|
b.opts.After = after
|
|
|
|
return b
|
|
|
|
}
|
2020-05-05 08:49:33 -04:00
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Before sets the before option.
|
|
|
|
func (b *UserSearchBuilder) Before(before string) *UserSearchBuilder {
|
|
|
|
b.opts.Before = before
|
|
|
|
return b
|
|
|
|
}
|
2020-05-05 08:49:33 -04:00
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Limit sets the limit option.
|
|
|
|
func (b *UserSearchBuilder) Limit(limit int) *UserSearchBuilder {
|
|
|
|
b.opts.Limit = limit
|
|
|
|
return b
|
2020-05-05 08:49:33 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
// Do conducts the search.
|
|
|
|
func (b *UserSearchBuilder) Do(ctx context.Context) (*Users, *Response, error) {
|
2020-05-04 20:51:16 -04:00
|
|
|
path := "search"
|
2020-05-16 21:46:16 -04:00
|
|
|
path, err := addOptions(path, b.opts)
|
2020-05-04 20:51:16 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
req, err := b.client.NewRequest(http.MethodGet, path, nil)
|
2020-05-04 20:51:16 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
root := new(rootListing)
|
2020-05-16 21:46:16 -04:00
|
|
|
resp, err := b.client.Do(ctx, req, root)
|
2020-05-04 20:51:16 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
2020-05-16 21:46:16 -04:00
|
|
|
return root.getUsers(), resp, nil
|
2020-05-04 20:51:16 -04:00
|
|
|
}
|