Get post/comments, unmarshal list of comments (replies)

Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
Vartan Benohanian
2020-05-16 11:38:47 -04:00
parent f2197d794c
commit 17b220b13c
4 changed files with 190 additions and 219 deletions
+68 -206
View File
@@ -7,13 +7,12 @@ import (
"net/http"
"net/url"
"strings"
"time"
)
// SubredditService handles communication with the subreddit
// related methods of the Reddit API
type SubredditService interface {
GetByName(ctx context.Context, name string) (*Subreddit, *Response, error)
GetByName(ctx context.Context, subreddit string) (*Subreddit, *Response, error)
GetPopular(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetNew(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
@@ -25,22 +24,20 @@ type SubredditService interface {
GetMineWhereModerator(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetMineWhereStreams(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetHotLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error)
GetBestLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error)
GetNewLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error)
GetRisingLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error)
GetControversialLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error)
GetTopLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error)
GetHotLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error)
GetBestLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error)
GetNewLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error)
GetRisingLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error)
GetControversialLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error)
GetTopLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error)
// GetSticky1(ctx context.Context, name string) (interface{}, *Response, error)
// GetSticky2(ctx context.Context, name string) (interface{}, *Response, error)
GetSticky1(ctx context.Context, subreddit string) (*LinkAndComments, *Response, error)
GetSticky2(ctx context.Context, subreddit string) (*LinkAndComments, *Response, error)
Subscribe(ctx context.Context, names ...string) (*Response, error)
Subscribe(ctx context.Context, subreddits ...string) (*Response, error)
SubscribeByID(ctx context.Context, ids ...string) (*Response, error)
Unsubscribe(ctx context.Context, names ...string) (*Response, error)
Unsubscribe(ctx context.Context, subreddits ...string) (*Response, error)
UnsubscribeByID(ctx context.Context, ids ...string) (*Response, error)
StreamLinks(ctx context.Context, names ...string) (<-chan Link, chan<- bool, error)
}
// SubredditServiceOp implements the SubredditService interface
@@ -51,12 +48,12 @@ type SubredditServiceOp struct {
var _ SubredditService = &SubredditServiceOp{}
// GetByName gets a subreddit by name
func (s *SubredditServiceOp) GetByName(ctx context.Context, name string) (*Subreddit, *Response, error) {
if name == "" {
func (s *SubredditServiceOp) GetByName(ctx context.Context, subreddit string) (*Subreddit, *Response, error) {
if subreddit == "" {
return nil, nil, errors.New("empty subreddit name provided")
}
path := fmt.Sprintf("r/%s/about", name)
path := fmt.Sprintf("r/%s/about", subreddit)
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
@@ -112,68 +109,61 @@ func (s *SubredditServiceOp) GetMineWhereStreams(ctx context.Context, opts *List
}
// GetHotLinks returns the hot links
// If no subreddit names are provided, then it runs the search against all those the client is subscribed to
// If no subreddit are provided, then it runs the search against all those the client is subscribed to
// IMPORTANT: for subreddits, this will include the stickied posts (if any)
// PLUS the number of posts from the limit parameter (which is 25 by default)
func (s *SubredditServiceOp) GetHotLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortHot, opts, names...)
func (s *SubredditServiceOp) GetHotLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortHot, opts, subreddits...)
}
// GetBestLinks returns the best links
// If no subreddit names are provided, then it runs the search against all those the client is subscribed to
// If no subreddit are provided, then it runs the search against all those the client is subscribed to
// IMPORTANT: for subreddits, this will include the stickied posts (if any)
// PLUS the number of posts from the limit parameter (which is 25 by default)
func (s *SubredditServiceOp) GetBestLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortBest, opts, names...)
func (s *SubredditServiceOp) GetBestLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortBest, opts, subreddits...)
}
// GetNewLinks returns the new links
// If no subreddit names are provided, then it runs the search against all those the client is subscribed to
func (s *SubredditServiceOp) GetNewLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortNew, opts, names...)
// If no subreddit are provided, then it runs the search against all those the client is subscribed to
func (s *SubredditServiceOp) GetNewLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortNew, opts, subreddits...)
}
// GetRisingLinks returns the rising links
// If no subreddit names are provided, then it runs the search against all those the client is subscribed to
func (s *SubredditServiceOp) GetRisingLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortRising, opts, names...)
// If no subreddit are provided, then it runs the search against all those the client is subscribed to
func (s *SubredditServiceOp) GetRisingLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortRising, opts, subreddits...)
}
// GetControversialLinks returns the controversial links
// If no subreddit names are provided, then it runs the search against all those the client is subscribed to
func (s *SubredditServiceOp) GetControversialLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortControversial, opts, names...)
// If no subreddit are provided, then it runs the search against all those the client is subscribed to
func (s *SubredditServiceOp) GetControversialLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortControversial, opts, subreddits...)
}
// GetTopLinks returns the top links
// If no subreddit names are provided, then it runs the search against all those the client is subscribed to
func (s *SubredditServiceOp) GetTopLinks(ctx context.Context, opts *ListOptions, names ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortTop, opts, names...)
// If no subreddit are provided, then it runs the search against all those the client is subscribed to
func (s *SubredditServiceOp) GetTopLinks(ctx context.Context, opts *ListOptions, subreddits ...string) (*Links, *Response, error) {
return s.getLinks(ctx, sortTop, opts, subreddits...)
}
type sticky int
// GetSticky1 returns the first stickied post on a subreddit (if it exists)
func (s *SubredditServiceOp) GetSticky1(ctx context.Context, name string) (*LinkAndComments, *Response, error) {
return s.getSticky(ctx, name, sticky1)
}
const (
sticky1 sticky = iota + 1
sticky2
)
// GetSticky2 returns the second stickied post on a subreddit (if it exists)
func (s *SubredditServiceOp) GetSticky2(ctx context.Context, name string) (*LinkAndComments, *Response, error) {
return s.getSticky(ctx, name, sticky2)
}
// // GetSticky1 returns the first stickied post on a subreddit (if it exists)
// func (s *SubredditServiceOp) GetSticky1(ctx context.Context, name string) (interface{}, *Response, error) {
// return s.getSticky(ctx, name, sticky1)
// }
// // GetSticky2 returns the second stickied post on a subreddit (if it exists)
// func (s *SubredditServiceOp) GetSticky2(ctx context.Context, name string) (interface{}, *Response, error) {
// return s.getSticky(ctx, name, sticky2)
// }
// Subscribe subscribes to subreddits based on their name
// Subscribe subscribes to subreddits based on their names
// Returns {} on success
func (s *SubredditServiceOp) Subscribe(ctx context.Context, names ...string) (*Response, error) {
func (s *SubredditServiceOp) Subscribe(ctx context.Context, subreddits ...string) (*Response, error) {
form := url.Values{}
form.Set("action", "sub")
form.Set("sr_name", strings.Join(names, ","))
form.Set("sr_name", strings.Join(subreddits, ","))
return s.handleSubscription(ctx, form)
}
@@ -186,12 +176,12 @@ func (s *SubredditServiceOp) SubscribeByID(ctx context.Context, ids ...string) (
return s.handleSubscription(ctx, form)
}
// Unsubscribe unsubscribes from subreddits
// Unsubscribe unsubscribes from subreddits based on their names
// Returns {} on success
func (s *SubredditServiceOp) Unsubscribe(ctx context.Context, names ...string) (*Response, error) {
func (s *SubredditServiceOp) Unsubscribe(ctx context.Context, subreddits ...string) (*Response, error) {
form := url.Values{}
form.Set("action", "unsub")
form.Set("sr_name", strings.Join(names, ","))
form.Set("sr_name", strings.Join(subreddits, ","))
return s.handleSubscription(ctx, form)
}
@@ -247,10 +237,10 @@ func (s *SubredditServiceOp) getSubreddits(ctx context.Context, path string, opt
return l, resp, nil
}
func (s *SubredditServiceOp) getLinks(ctx context.Context, sort sort, opts *ListOptions, names ...string) (*Links, *Response, error) {
func (s *SubredditServiceOp) getLinks(ctx context.Context, sort sort, opts *ListOptions, subreddits ...string) (*Links, *Response, error) {
path := sorts[sort]
if len(names) > 0 {
path = fmt.Sprintf("r/%s/%s", strings.Join(names, "+"), sorts[sort])
if len(subreddits) > 0 {
path = fmt.Sprintf("r/%s/%s", strings.Join(subreddits, "+"), sorts[sort])
}
path, err := addOptions(path, opts)
@@ -280,159 +270,31 @@ func (s *SubredditServiceOp) getLinks(ctx context.Context, sort sort, opts *List
return l, resp, nil
}
// getSticky returns one of the 2 stickied posts of the subreddit
// getSticky returns one of the 2 stickied posts of the subreddit (if they exist)
// Num should be equal to 1 or 2, depending on which one you want
// If it's <= 1, it's 1
// If it's >= 2, it's 2
// todo
// func (s *SubredditServiceOp) getSticky(ctx context.Context, name string, num sticky) (interface{}, *Response, error) {
// type query struct {
// Num sticky `url:"num"`
// }
// path := fmt.Sprintf("r/%s/about/sticky", name)
// path, err := addOptions(path, query{num})
// if err != nil {
// return nil, nil, err
// }
// req, err := s.client.NewRequest(http.MethodGet, path, nil)
// if err != nil {
// return nil, nil, err
// }
// var root []rootListing
// resp, err := s.client.Do(ctx, req, &root)
// if err != nil {
// return nil, resp, err
// }
// // test, _ := json.MarshalIndent(root, "", " ")
// // fmt.Println(string(test))
// linkRoot := new(linkRoot)
// link := root[0].Data.Children[0]
// byteValue, err := json.Marshal(link)
// if err != nil {
// return nil, resp, err
// }
// err = json.Unmarshal(byteValue, linkRoot)
// if err != nil {
// return nil, resp, err
// }
// // these are all the comments in the post
// comments := root[1].Data.Children
// var commentsRoot []commentRoot
// byteValue, err = json.Marshal(comments)
// if err != nil {
// return nil, resp, err
// }
// err = json.Unmarshal(byteValue, &commentsRoot)
// if err != nil {
// return nil, resp, err
// }
// test, _ := json.MarshalIndent(commentsRoot, "", " ")
// fmt.Println(string(test))
// for _, comment := range commentsRoot {
// if string(comment.Data.RepliesRaw) == `""` {
// comment.Data.Replies = nil
// continue
// }
// // var
// }
// return commentsRoot, resp, nil
// }
// func handleComments(comments []commentRoot) {
// for _, comment := range comments {
// if string(comment.Data.RepliesRaw) == `""` {
// comment.Data.Replies = nil
// continue
// }
// }
// }
// StreamLinks returns a channel that receives new submissions from the subreddits
// To stop the stream, simply send a bool value to the stop channel
func (s *SubredditServiceOp) StreamLinks(ctx context.Context, names ...string) (<-chan Link, chan<- bool, error) {
if len(names) == 0 {
return nil, nil, errors.New("must specify at least one subreddit")
func (s *SubredditServiceOp) getSticky(ctx context.Context, subreddit string, num sticky) (*LinkAndComments, *Response, error) {
type query struct {
Num sticky `url:"num"`
}
submissionCh := make(chan Link)
stop := make(chan bool, 1)
path := fmt.Sprintf("r/%s/about/sticky", subreddit)
path, err := addOptions(path, query{num})
if err != nil {
return nil, nil, err
}
go func() {
// todo: if the post with the before gets deleted, you keep getting 0 posts
var last *Timestamp
for {
select {
case <-stop:
close(submissionCh)
return
default:
sl, _, err := s.GetNewLinks(ctx, nil, names...)
if err != nil {
continue
}
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}
var newest *Timestamp
for i, submission := range sl.Links {
if i == 0 {
newest = submission.Created
}
if last == nil {
submissionCh <- submission
continue
}
if last.Before(*submission.Created) {
submissionCh <- submission
}
}
last = newest
}
<-time.After(time.Second * 3)
fmt.Println()
}
}()
root := new(LinkAndComments)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
// go func() {
// var before string
// for {
// select {
// case <-stop:
// close(submissionCh)
// return
// default:
// sl, _, err := s.GetSubmissions(ctx, SortNew, &ListOptions{Before: before}, names...)
// if err != nil {
// continue
// }
// fmt.Printf("Received %d posts\n", len(sl.Submissions))
// if len(sl.Submissions) == 0 {
// continue
// }
// for _, submission := range sl.Submissions {
// submissionCh <- submission
// }
// before = sl.Submissions[0].FullID
// }
// <-time.After(time.Second * 5)
// fmt.Println()
// }
// }()
return submissionCh, stop, nil
return root, resp, nil
}