2020-07-11 13:49:07 -04:00
|
|
|
package reddit
|
2020-05-03 19:17:39 -04:00
|
|
|
|
2020-05-16 11:38:47 -04:00
|
|
|
import (
|
|
|
|
"encoding/json"
|
2020-08-30 21:25:01 -04:00
|
|
|
"fmt"
|
2020-05-16 11:38:47 -04:00
|
|
|
)
|
2020-05-03 19:17:39 -04:00
|
|
|
|
|
|
|
const (
|
2020-09-21 23:28:05 -04:00
|
|
|
kindComment = "t1"
|
|
|
|
kindUser = "t2"
|
|
|
|
kindPost = "t3"
|
|
|
|
kindMessage = "t4"
|
|
|
|
kindSubreddit = "t5"
|
|
|
|
kindTrophy = "t6"
|
|
|
|
kindListing = "Listing"
|
|
|
|
kindSubredditSettings = "subreddit_settings"
|
|
|
|
kindKarmaList = "KarmaList"
|
|
|
|
kindTrophyList = "TrophyList"
|
|
|
|
kindUserList = "UserList"
|
|
|
|
kindMore = "more"
|
|
|
|
kindLiveThread = "LiveUpdateEvent"
|
|
|
|
kindLiveThreadUpdate = "LiveUpdate"
|
|
|
|
kindModAction = "modaction"
|
|
|
|
kindMulti = "LabeledMulti"
|
|
|
|
kindMultiDescription = "LabeledMultiDescription"
|
|
|
|
kindWikiPage = "wikipage"
|
|
|
|
kindWikiPageListing = "wikipagelisting"
|
|
|
|
kindWikiPageSettings = "wikipagesettings"
|
|
|
|
kindStyleSheet = "stylesheet"
|
2020-05-03 19:17:39 -04:00
|
|
|
)
|
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
type anchor interface {
|
|
|
|
After() string
|
|
|
|
Before() string
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:06:34 -04:00
|
|
|
// thing is an entity on Reddit.
|
2020-08-29 14:20:30 -04:00
|
|
|
// Its kind reprsents what it is and what is stored in the Data field.
|
2020-08-11 21:06:34 -04:00
|
|
|
// e.g. t1 = comment, t2 = user, t3 = post, etc.
|
|
|
|
type thing struct {
|
2020-08-30 21:25:01 -04:00
|
|
|
Kind string `json:"kind"`
|
|
|
|
Data interface{} `json:"data"`
|
|
|
|
}
|
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
func (t *thing) After() string {
|
|
|
|
if t == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
a, ok := t.Data.(anchor)
|
|
|
|
if !ok {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return a.After()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thing) Before() string {
|
|
|
|
if t == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
a, ok := t.Data.(anchor)
|
|
|
|
if !ok {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return a.Before()
|
|
|
|
}
|
|
|
|
|
2020-08-30 21:25:01 -04:00
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
|
|
func (t *thing) UnmarshalJSON(b []byte) error {
|
|
|
|
root := new(struct {
|
|
|
|
Kind string `json:"kind"`
|
|
|
|
Data json.RawMessage `json:"data"`
|
|
|
|
})
|
|
|
|
|
|
|
|
err := json.Unmarshal(b, root)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Kind = root.Kind
|
|
|
|
var v interface{}
|
|
|
|
|
|
|
|
switch t.Kind {
|
2020-09-01 22:35:28 -04:00
|
|
|
case kindListing:
|
|
|
|
v = new(listing)
|
2020-08-30 21:25:01 -04:00
|
|
|
case kindComment:
|
|
|
|
v = new(Comment)
|
|
|
|
case kindMore:
|
|
|
|
v = new(More)
|
2020-08-31 12:51:45 -04:00
|
|
|
case kindUser:
|
2020-08-30 21:25:01 -04:00
|
|
|
v = new(User)
|
|
|
|
case kindPost:
|
|
|
|
v = new(Post)
|
|
|
|
case kindSubreddit:
|
|
|
|
v = new(Subreddit)
|
2020-09-21 23:28:05 -04:00
|
|
|
case kindSubredditSettings:
|
|
|
|
v = new(SubredditSettings)
|
2020-09-15 21:41:12 -04:00
|
|
|
case kindLiveThread:
|
|
|
|
v = new(LiveThread)
|
2020-09-18 01:20:18 -04:00
|
|
|
case kindLiveThreadUpdate:
|
|
|
|
v = new(LiveThreadUpdate)
|
2020-08-30 21:25:01 -04:00
|
|
|
case kindModAction:
|
|
|
|
v = new(ModAction)
|
2020-08-31 12:51:45 -04:00
|
|
|
case kindMulti:
|
|
|
|
v = new(Multi)
|
2020-09-09 23:02:06 -04:00
|
|
|
case kindMultiDescription:
|
|
|
|
v = new(rootMultiDescription)
|
2020-09-01 19:30:05 -04:00
|
|
|
case kindTrophy:
|
|
|
|
v = new(Trophy)
|
|
|
|
case kindTrophyList:
|
|
|
|
v = new(trophyList)
|
2020-09-01 22:35:28 -04:00
|
|
|
case kindKarmaList:
|
|
|
|
v = new([]*SubredditKarma)
|
2020-09-04 23:42:24 -04:00
|
|
|
case kindWikiPage:
|
|
|
|
v = new(WikiPage)
|
2020-09-03 23:25:16 -04:00
|
|
|
case kindWikiPageListing:
|
|
|
|
v = new([]string)
|
|
|
|
case kindWikiPageSettings:
|
|
|
|
v = new(WikiPageSettings)
|
2020-09-13 21:43:59 -04:00
|
|
|
case kindStyleSheet:
|
|
|
|
v = new(SubredditStyleSheet)
|
2020-08-30 21:25:01 -04:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("unrecognized kind: %q", t.Kind)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(root.Data, v)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Data = v
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
func (t *thing) Listing() (v *listing, ok bool) {
|
|
|
|
v, ok = t.Data.(*listing)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
func (t *thing) Comment() (v *Comment, ok bool) {
|
|
|
|
v, ok = t.Data.(*Comment)
|
|
|
|
return
|
2020-08-30 21:25:01 -04:00
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
func (t *thing) More() (v *More, ok bool) {
|
|
|
|
v, ok = t.Data.(*More)
|
|
|
|
return
|
2020-08-30 21:25:01 -04:00
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
func (t *thing) User() (v *User, ok bool) {
|
|
|
|
v, ok = t.Data.(*User)
|
|
|
|
return
|
2020-08-30 21:25:01 -04:00
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
func (t *thing) Post() (v *Post, ok bool) {
|
|
|
|
v, ok = t.Data.(*Post)
|
|
|
|
return
|
2020-08-30 21:25:01 -04:00
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
func (t *thing) Subreddit() (v *Subreddit, ok bool) {
|
|
|
|
v, ok = t.Data.(*Subreddit)
|
|
|
|
return
|
2020-08-30 21:25:01 -04:00
|
|
|
}
|
|
|
|
|
2020-09-21 23:28:05 -04:00
|
|
|
func (t *thing) SubredditSettings() (v *SubredditSettings, ok bool) {
|
|
|
|
v, ok = t.Data.(*SubredditSettings)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-15 21:41:12 -04:00
|
|
|
func (t *thing) LiveThread() (v *LiveThread, ok bool) {
|
|
|
|
v, ok = t.Data.(*LiveThread)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-18 01:20:18 -04:00
|
|
|
func (t *thing) LiveThreadUpdate() (v *LiveThreadUpdate, ok bool) {
|
|
|
|
v, ok = t.Data.(*LiveThreadUpdate)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
func (t *thing) ModAction() (v *ModAction, ok bool) {
|
|
|
|
v, ok = t.Data.(*ModAction)
|
|
|
|
return
|
2020-05-03 19:17:39 -04:00
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
func (t *thing) Multi() (v *Multi, ok bool) {
|
|
|
|
v, ok = t.Data.(*Multi)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-09 23:02:06 -04:00
|
|
|
func (t *thing) MultiDescription() (s string, ok bool) {
|
|
|
|
v, ok := t.Data.(*rootMultiDescription)
|
|
|
|
if ok {
|
|
|
|
s = v.Body
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
func (t *thing) Trophy() (v *Trophy, ok bool) {
|
|
|
|
v, ok = t.Data.(*Trophy)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thing) TrophyList() ([]*Trophy, bool) {
|
|
|
|
v, ok := t.Data.(*trophyList)
|
2020-09-01 22:35:28 -04:00
|
|
|
if !ok {
|
|
|
|
return nil, ok
|
|
|
|
}
|
2020-09-01 19:30:05 -04:00
|
|
|
return *v, ok
|
2020-08-31 12:51:45 -04:00
|
|
|
}
|
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
func (t *thing) Karma() ([]*SubredditKarma, bool) {
|
|
|
|
v, ok := t.Data.(*[]*SubredditKarma)
|
|
|
|
if !ok {
|
|
|
|
return nil, ok
|
|
|
|
}
|
|
|
|
return *v, ok
|
2020-05-03 19:17:39 -04:00
|
|
|
}
|
|
|
|
|
2020-09-04 23:42:24 -04:00
|
|
|
func (t *thing) WikiPage() (v *WikiPage, ok bool) {
|
|
|
|
v, ok = t.Data.(*WikiPage)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-03 23:25:16 -04:00
|
|
|
func (t *thing) WikiPages() ([]string, bool) {
|
|
|
|
v, ok := t.Data.(*[]string)
|
|
|
|
if !ok {
|
|
|
|
return nil, ok
|
|
|
|
}
|
|
|
|
return *v, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thing) WikiPageSettings() (v *WikiPageSettings, ok bool) {
|
|
|
|
v, ok = t.Data.(*WikiPageSettings)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-13 21:43:59 -04:00
|
|
|
func (t *thing) StyleSheet() (v *SubredditStyleSheet, ok bool) {
|
|
|
|
v, ok = t.Data.(*SubredditStyleSheet)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-08-30 21:25:01 -04:00
|
|
|
// listing is a list of things coming from the Reddit API.
|
2020-08-29 14:20:30 -04:00
|
|
|
// It also contains the after/before anchors useful for subsequent requests.
|
2020-08-11 21:06:34 -04:00
|
|
|
type listing struct {
|
2020-09-01 22:35:28 -04:00
|
|
|
things things
|
2020-08-29 14:20:30 -04:00
|
|
|
after string
|
|
|
|
before string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *listing) After() string {
|
|
|
|
return l.after
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *listing) Before() string {
|
|
|
|
return l.before
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
|
|
func (l *listing) UnmarshalJSON(b []byte) error {
|
|
|
|
root := new(struct {
|
2020-09-01 22:35:28 -04:00
|
|
|
Things things `json:"children"`
|
|
|
|
After string `json:"after"`
|
|
|
|
Before string `json:"before"`
|
2020-08-29 14:20:30 -04:00
|
|
|
})
|
|
|
|
|
|
|
|
err := json.Unmarshal(b, root)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
l.things = root.Things
|
|
|
|
l.after = root.After
|
|
|
|
l.before = root.Before
|
2020-08-29 14:20:30 -04:00
|
|
|
|
|
|
|
return nil
|
2020-05-03 19:17:39 -04:00
|
|
|
}
|
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
func (l *listing) Comments() []*Comment {
|
|
|
|
if l == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return l.things.Comments
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *listing) Mores() []*More {
|
|
|
|
if l == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return l.things.Mores
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *listing) Users() []*User {
|
|
|
|
if l == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return l.things.Users
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *listing) Posts() []*Post {
|
|
|
|
if l == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return l.things.Posts
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *listing) Subreddits() []*Subreddit {
|
|
|
|
if l == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return l.things.Subreddits
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *listing) ModActions() []*ModAction {
|
|
|
|
if l == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return l.things.ModActions
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *listing) Multis() []*Multi {
|
|
|
|
if l == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return l.things.Multis
|
|
|
|
}
|
|
|
|
|
2020-09-17 23:33:47 -04:00
|
|
|
func (l *listing) LiveThreads() []*LiveThread {
|
|
|
|
if l == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return l.things.LiveThreads
|
|
|
|
}
|
|
|
|
|
2020-09-18 01:20:18 -04:00
|
|
|
func (l *listing) LiveThreadUpdates() []*LiveThreadUpdate {
|
|
|
|
if l == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return l.things.LiveThreadUpdates
|
|
|
|
}
|
|
|
|
|
2020-08-11 21:06:34 -04:00
|
|
|
type things struct {
|
2020-09-18 01:20:18 -04:00
|
|
|
Comments []*Comment
|
|
|
|
Mores []*More
|
|
|
|
Users []*User
|
|
|
|
Posts []*Post
|
|
|
|
Subreddits []*Subreddit
|
|
|
|
ModActions []*ModAction
|
|
|
|
Multis []*Multi
|
|
|
|
LiveThreads []*LiveThread
|
|
|
|
LiveThreadUpdates []*LiveThreadUpdate
|
2020-05-03 19:17:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
2020-08-11 21:06:34 -04:00
|
|
|
func (t *things) UnmarshalJSON(b []byte) error {
|
2020-07-30 13:01:18 -04:00
|
|
|
var things []thing
|
|
|
|
if err := json.Unmarshal(b, &things); err != nil {
|
2020-05-03 19:17:39 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-30 21:25:01 -04:00
|
|
|
t.add(things...)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *things) add(things ...thing) {
|
2020-07-30 13:01:18 -04:00
|
|
|
for _, thing := range things {
|
2020-08-30 21:25:01 -04:00
|
|
|
switch v := thing.Data.(type) {
|
|
|
|
case *Comment:
|
|
|
|
t.Comments = append(t.Comments, v)
|
|
|
|
case *More:
|
|
|
|
t.Mores = append(t.Mores, v)
|
|
|
|
case *User:
|
|
|
|
t.Users = append(t.Users, v)
|
|
|
|
case *Post:
|
|
|
|
t.Posts = append(t.Posts, v)
|
|
|
|
case *Subreddit:
|
|
|
|
t.Subreddits = append(t.Subreddits, v)
|
|
|
|
case *ModAction:
|
|
|
|
t.ModActions = append(t.ModActions, v)
|
2020-08-31 12:51:45 -04:00
|
|
|
case *Multi:
|
|
|
|
t.Multis = append(t.Multis, v)
|
2020-09-17 23:33:47 -04:00
|
|
|
case *LiveThread:
|
|
|
|
t.LiveThreads = append(t.LiveThreads, v)
|
2020-09-18 01:20:18 -04:00
|
|
|
case *LiveThreadUpdate:
|
|
|
|
t.LiveThreadUpdates = append(t.LiveThreadUpdates, v)
|
2020-05-03 19:17:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
type trophyList []*Trophy
|
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
|
|
func (l *trophyList) UnmarshalJSON(b []byte) error {
|
|
|
|
root := new(struct {
|
|
|
|
Trophies []thing `json:"trophies"`
|
|
|
|
})
|
|
|
|
|
|
|
|
err := json.Unmarshal(b, root)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-09-29 13:52:12 -04:00
|
|
|
*l = make(trophyList, 0, len(root.Trophies))
|
2020-09-01 19:30:05 -04:00
|
|
|
for _, thing := range root.Trophies {
|
|
|
|
if trophy, ok := thing.Trophy(); ok {
|
|
|
|
*l = append(*l, trophy)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-17 22:01:04 -04:00
|
|
|
// Comment is a comment posted by a user.
|
2020-05-03 19:17:39 -04:00
|
|
|
type Comment struct {
|
2020-06-17 22:35:19 -04:00
|
|
|
ID string `json:"id,omitempty"`
|
|
|
|
FullID string `json:"name,omitempty"`
|
2020-05-18 23:11:47 -04:00
|
|
|
Created *Timestamp `json:"created_utc,omitempty"`
|
|
|
|
Edited *Timestamp `json:"edited,omitempty"`
|
|
|
|
|
2020-08-19 09:14:39 -04:00
|
|
|
ParentID string `json:"parent_id,omitempty"`
|
|
|
|
Permalink string `json:"permalink,omitempty"`
|
2020-06-17 22:35:19 -04:00
|
|
|
|
2020-05-03 19:17:39 -04:00
|
|
|
Body string `json:"body,omitempty"`
|
|
|
|
Author string `json:"author,omitempty"`
|
|
|
|
AuthorID string `json:"author_fullname,omitempty"`
|
|
|
|
AuthorFlairText string `json:"author_flair_text,omitempty"`
|
|
|
|
AuthorFlairID string `json:"author_flair_template_id,omitempty"`
|
|
|
|
|
2020-08-17 22:01:04 -04:00
|
|
|
SubredditName string `json:"subreddit,omitempty"`
|
2020-05-03 19:17:39 -04:00
|
|
|
SubredditNamePrefixed string `json:"subreddit_name_prefixed,omitempty"`
|
|
|
|
SubredditID string `json:"subreddit_id,omitempty"`
|
|
|
|
|
2020-08-02 15:33:06 -04:00
|
|
|
// Indicates if you've upvote/downvoted (true/false).
|
|
|
|
// If neither, it will be nil.
|
2020-05-03 20:32:08 -04:00
|
|
|
Likes *bool `json:"likes"`
|
|
|
|
|
2020-05-03 19:17:39 -04:00
|
|
|
Score int `json:"score"`
|
|
|
|
Controversiality int `json:"controversiality"`
|
|
|
|
|
2020-06-22 21:52:34 -04:00
|
|
|
PostID string `json:"link_id,omitempty"`
|
2020-08-17 22:01:04 -04:00
|
|
|
// This doesn't appear consistently.
|
2020-06-29 21:38:47 -04:00
|
|
|
PostTitle string `json:"link_title,omitempty"`
|
2020-08-17 22:01:04 -04:00
|
|
|
// This doesn't appear consistently.
|
2020-06-29 21:38:47 -04:00
|
|
|
PostPermalink string `json:"link_permalink,omitempty"`
|
2020-08-17 22:01:04 -04:00
|
|
|
// This doesn't appear consistently.
|
2020-06-29 21:38:47 -04:00
|
|
|
PostAuthor string `json:"link_author,omitempty"`
|
2020-08-17 22:01:04 -04:00
|
|
|
// This doesn't appear consistently.
|
2020-08-02 15:33:06 -04:00
|
|
|
PostNumComments *int `json:"num_comments,omitempty"`
|
2020-05-03 19:17:39 -04:00
|
|
|
|
|
|
|
IsSubmitter bool `json:"is_submitter"`
|
|
|
|
ScoreHidden bool `json:"score_hidden"`
|
|
|
|
Saved bool `json:"saved"`
|
|
|
|
Stickied bool `json:"stickied"`
|
|
|
|
Locked bool `json:"locked"`
|
|
|
|
CanGild bool `json:"can_gild"`
|
|
|
|
NSFW bool `json:"over_18"`
|
|
|
|
|
2020-07-19 22:03:37 -04:00
|
|
|
Replies Replies `json:"replies"`
|
2020-05-16 11:38:47 -04:00
|
|
|
}
|
|
|
|
|
2020-08-14 00:04:47 -04:00
|
|
|
// HasMore determines whether the comment has more replies to load in its reply tree.
|
|
|
|
func (c *Comment) HasMore() bool {
|
2020-08-12 22:55:00 -04:00
|
|
|
return c.Replies.More != nil && len(c.Replies.More.Children) > 0
|
2020-07-29 14:11:06 -04:00
|
|
|
}
|
|
|
|
|
2020-08-14 00:04:47 -04:00
|
|
|
// addCommentToReplies traverses the comment tree to find the one
|
|
|
|
// that the 2nd comment is replying to. It then adds it to its replies.
|
|
|
|
func (c *Comment) addCommentToReplies(comment *Comment) {
|
|
|
|
if c.FullID == comment.ParentID {
|
|
|
|
c.Replies.Comments = append(c.Replies.Comments, comment)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, reply := range c.Replies.Comments {
|
|
|
|
reply.addCommentToReplies(comment)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Comment) addMoreToReplies(more *More) {
|
|
|
|
if c.FullID == more.ParentID {
|
|
|
|
c.Replies.More = more
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, reply := range c.Replies.Comments {
|
|
|
|
reply.addMoreToReplies(more)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-17 17:04:28 -04:00
|
|
|
// Replies holds replies to a comment.
|
|
|
|
// It contains both comments and "more" comments, which are entrypoints to other
|
|
|
|
// comments that were left out.
|
|
|
|
type Replies struct {
|
2020-08-12 22:55:00 -04:00
|
|
|
Comments []*Comment `json:"comments,omitempty"`
|
2020-08-14 00:04:47 -04:00
|
|
|
More *More `json:"-"`
|
2020-07-17 17:04:28 -04:00
|
|
|
}
|
2020-05-16 11:38:47 -04:00
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
|
|
func (r *Replies) UnmarshalJSON(data []byte) error {
|
|
|
|
// if a comment has no replies, its "replies" field is set to ""
|
|
|
|
if string(data) == `""` {
|
2020-07-17 17:04:28 -04:00
|
|
|
r = nil
|
2020-05-16 11:38:47 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
root := new(thing)
|
2020-05-16 11:38:47 -04:00
|
|
|
err := json.Unmarshal(data, root)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
listing, _ := root.Listing()
|
|
|
|
|
|
|
|
r.Comments = listing.Comments()
|
|
|
|
if len(listing.Mores()) > 0 {
|
|
|
|
r.More = listing.Mores()[0]
|
2020-08-29 14:20:30 -04:00
|
|
|
}
|
2020-07-17 17:04:28 -04:00
|
|
|
|
2020-05-16 11:38:47 -04:00
|
|
|
return nil
|
2020-05-03 19:17:39 -04:00
|
|
|
}
|
|
|
|
|
2020-08-14 00:04:47 -04:00
|
|
|
// MarshalJSON implements the json.Marshaler interface.
|
|
|
|
func (r *Replies) MarshalJSON() ([]byte, error) {
|
|
|
|
if r == nil || len(r.Comments) == 0 {
|
|
|
|
return []byte(`null`), nil
|
|
|
|
}
|
|
|
|
return json.Marshal(r.Comments)
|
|
|
|
}
|
|
|
|
|
2020-08-17 22:01:04 -04:00
|
|
|
// More holds information used to retrieve additional comments omitted from a base comment tree.
|
2020-07-17 17:04:28 -04:00
|
|
|
type More struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
FullID string `json:"name"`
|
|
|
|
ParentID string `json:"parent_id"`
|
|
|
|
// Total number of replies to the parent + replies to those replies (recursively).
|
|
|
|
Count int `json:"count"`
|
|
|
|
// Number of comment nodes from the parent down to the furthest comment node.
|
|
|
|
Depth int `json:"depth"`
|
|
|
|
Children []string `json:"children"`
|
|
|
|
}
|
|
|
|
|
2020-06-22 21:52:34 -04:00
|
|
|
// Post is a submitted post on Reddit.
|
|
|
|
type Post struct {
|
2020-05-03 19:17:39 -04:00
|
|
|
ID string `json:"id,omitempty"`
|
|
|
|
FullID string `json:"name,omitempty"`
|
|
|
|
Created *Timestamp `json:"created_utc,omitempty"`
|
|
|
|
Edited *Timestamp `json:"edited,omitempty"`
|
|
|
|
|
2020-08-19 09:14:39 -04:00
|
|
|
Permalink string `json:"permalink,omitempty"`
|
|
|
|
URL string `json:"url,omitempty"`
|
2020-05-03 19:17:39 -04:00
|
|
|
|
|
|
|
Title string `json:"title,omitempty"`
|
|
|
|
Body string `json:"selftext,omitempty"`
|
|
|
|
|
2020-09-07 21:24:14 -04:00
|
|
|
// Indicates if you've upvoted/downvoted (true/false).
|
2020-08-02 15:33:06 -04:00
|
|
|
// If neither, it will be nil.
|
2020-05-03 20:32:08 -04:00
|
|
|
Likes *bool `json:"likes"`
|
|
|
|
|
2020-05-04 20:51:16 -04:00
|
|
|
Score int `json:"score"`
|
|
|
|
UpvoteRatio float32 `json:"upvote_ratio"`
|
|
|
|
NumberOfComments int `json:"num_comments"`
|
2020-05-03 19:17:39 -04:00
|
|
|
|
|
|
|
SubredditName string `json:"subreddit,omitempty"`
|
|
|
|
SubredditNamePrefixed string `json:"subreddit_name_prefixed,omitempty"`
|
2020-08-17 22:01:04 -04:00
|
|
|
SubredditID string `json:"subreddit_id,omitempty"`
|
2020-09-07 21:24:14 -04:00
|
|
|
SubredditSubscribers int `json:"subreddit_subscribers"`
|
2020-05-03 19:17:39 -04:00
|
|
|
|
2020-08-17 22:01:04 -04:00
|
|
|
Author string `json:"author,omitempty"`
|
|
|
|
AuthorID string `json:"author_fullname,omitempty"`
|
2020-05-03 19:17:39 -04:00
|
|
|
|
|
|
|
Spoiler bool `json:"spoiler"`
|
|
|
|
Locked bool `json:"locked"`
|
|
|
|
NSFW bool `json:"over_18"`
|
|
|
|
IsSelfPost bool `json:"is_self"`
|
|
|
|
Saved bool `json:"saved"`
|
|
|
|
Stickied bool `json:"stickied"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subreddit holds information about a subreddit
|
|
|
|
type Subreddit struct {
|
|
|
|
ID string `json:"id,omitempty"`
|
|
|
|
FullID string `json:"name,omitempty"`
|
|
|
|
Created *Timestamp `json:"created_utc,omitempty"`
|
|
|
|
|
|
|
|
URL string `json:"url,omitempty"`
|
|
|
|
Name string `json:"display_name,omitempty"`
|
|
|
|
NamePrefixed string `json:"display_name_prefixed,omitempty"`
|
|
|
|
Title string `json:"title,omitempty"`
|
2020-06-20 23:04:01 -04:00
|
|
|
Description string `json:"public_description,omitempty"`
|
2020-05-03 19:17:39 -04:00
|
|
|
Type string `json:"subreddit_type,omitempty"`
|
|
|
|
SuggestedCommentSort string `json:"suggested_comment_sort,omitempty"`
|
|
|
|
|
|
|
|
Subscribers int `json:"subscribers"`
|
|
|
|
ActiveUserCount *int `json:"active_user_count,omitempty"`
|
|
|
|
NSFW bool `json:"over18"`
|
|
|
|
UserIsMod bool `json:"user_is_moderator"`
|
2020-08-19 21:37:08 -04:00
|
|
|
Subscribed bool `json:"user_is_subscriber"`
|
2020-07-06 22:10:47 -04:00
|
|
|
Favorite bool `json:"user_has_favorited"`
|
2020-05-03 19:17:39 -04:00
|
|
|
}
|
|
|
|
|
2020-07-29 14:11:06 -04:00
|
|
|
// PostAndComments is a post and its comments.
|
|
|
|
type PostAndComments struct {
|
2020-08-18 23:12:35 -04:00
|
|
|
Post *Post `json:"post"`
|
|
|
|
Comments []*Comment `json:"comments"`
|
|
|
|
More *More `json:"-"`
|
2020-05-16 11:38:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
|
|
// When getting a sticky post, you get an array of 2 Listings
|
|
|
|
// The 1st one contains the single post in its children array
|
|
|
|
// The 2nd one contains the comments to the post
|
2020-07-29 14:11:06 -04:00
|
|
|
func (pc *PostAndComments) UnmarshalJSON(data []byte) error {
|
2020-09-01 22:35:28 -04:00
|
|
|
var root [2]thing
|
2020-05-16 11:38:47 -04:00
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
err := json.Unmarshal(data, &root)
|
2020-05-16 11:38:47 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-09-01 22:35:28 -04:00
|
|
|
listing1, _ := root[0].Listing()
|
|
|
|
listing2, _ := root[1].Listing()
|
|
|
|
|
|
|
|
pc.Post = listing1.Posts()[0]
|
|
|
|
pc.Comments = listing2.Comments()
|
|
|
|
if len(listing2.Mores()) > 0 {
|
|
|
|
pc.More = listing2.Mores()[0]
|
2020-08-29 14:20:30 -04:00
|
|
|
}
|
2020-05-16 11:38:47 -04:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2020-07-29 14:11:06 -04:00
|
|
|
|
2020-08-14 00:04:47 -04:00
|
|
|
// HasMore determines whether the post has more replies to load in its reply tree.
|
|
|
|
func (pc *PostAndComments) HasMore() bool {
|
2020-08-18 23:12:35 -04:00
|
|
|
return pc.More != nil && len(pc.More.Children) > 0
|
2020-08-14 00:04:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pc *PostAndComments) addCommentToTree(comment *Comment) {
|
|
|
|
if pc.Post.FullID == comment.ParentID {
|
|
|
|
pc.Comments = append(pc.Comments, comment)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, reply := range pc.Comments {
|
|
|
|
reply.addCommentToReplies(comment)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pc *PostAndComments) addMoreToTree(more *More) {
|
|
|
|
if pc.Post.FullID == more.ParentID {
|
2020-08-18 23:12:35 -04:00
|
|
|
pc.More = more
|
2020-08-14 00:04:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, reply := range pc.Comments {
|
|
|
|
reply.addMoreToReplies(more)
|
|
|
|
}
|
2020-07-29 14:11:06 -04:00
|
|
|
}
|