reddit.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package feed
  2. import (
  3. "fmt"
  4. "html"
  5. "net/http"
  6. "net/url"
  7. "strings"
  8. "time"
  9. )
  10. type subredditResponseJson struct {
  11. Data struct {
  12. Children []struct {
  13. Data struct {
  14. Id string `json:"id"`
  15. Title string `json:"title"`
  16. Upvotes int `json:"ups"`
  17. Url string `json:"url"`
  18. Time float64 `json:"created"`
  19. CommentsCount int `json:"num_comments"`
  20. Domain string `json:"domain"`
  21. Permalink string `json:"permalink"`
  22. Stickied bool `json:"stickied"`
  23. Pinned bool `json:"pinned"`
  24. IsSelf bool `json:"is_self"`
  25. Thumbnail string `json:"thumbnail"`
  26. Flair string `json:"link_flair_text"`
  27. ParentList []struct {
  28. Id string `json:"id"`
  29. Subreddit string `json:"subreddit"`
  30. Permalink string `json:"permalink"`
  31. } `json:"crosspost_parent_list"`
  32. } `json:"data"`
  33. } `json:"children"`
  34. } `json:"data"`
  35. }
  36. func templateRedditCommentsURL(template, subreddit, postId, postPath string) string {
  37. template = strings.ReplaceAll(template, "{SUBREDDIT}", subreddit)
  38. template = strings.ReplaceAll(template, "{POST-ID}", postId)
  39. template = strings.ReplaceAll(template, "{POST-PATH}", strings.TrimLeft(postPath, "/"))
  40. return template
  41. }
  42. func FetchSubredditPosts(subreddit, sort, topPeriod, search, commentsUrlTemplate, requestUrlTemplate string, showFlairs bool) (ForumPosts, error) {
  43. query := url.Values{}
  44. var requestUrl string
  45. if search != "" {
  46. query.Set("q", search+" subreddit:"+subreddit)
  47. query.Set("sort", sort)
  48. }
  49. if sort == "top" {
  50. query.Set("t", topPeriod)
  51. }
  52. if search != "" {
  53. requestUrl = fmt.Sprintf("https://www.reddit.com/search.json?%s", query.Encode())
  54. } else {
  55. requestUrl = fmt.Sprintf("https://www.reddit.com/r/%s/%s.json?%s", subreddit, sort, query.Encode())
  56. }
  57. if requestUrlTemplate != "" {
  58. requestUrl = strings.ReplaceAll(requestUrlTemplate, "{REQUEST-URL}", requestUrl)
  59. }
  60. request, err := http.NewRequest("GET", requestUrl, nil)
  61. if err != nil {
  62. return nil, err
  63. }
  64. // Required to increase rate limit, otherwise Reddit randomly returns 429 even after just 2 requests
  65. addBrowserUserAgentHeader(request)
  66. responseJson, err := decodeJsonFromRequest[subredditResponseJson](defaultClient, request)
  67. if err != nil {
  68. return nil, err
  69. }
  70. if len(responseJson.Data.Children) == 0 {
  71. return nil, fmt.Errorf("no posts found")
  72. }
  73. posts := make(ForumPosts, 0, len(responseJson.Data.Children))
  74. for i := range responseJson.Data.Children {
  75. post := &responseJson.Data.Children[i].Data
  76. if post.Stickied || post.Pinned {
  77. continue
  78. }
  79. var commentsUrl string
  80. if commentsUrlTemplate == "" {
  81. commentsUrl = "https://www.reddit.com" + post.Permalink
  82. } else {
  83. commentsUrl = templateRedditCommentsURL(commentsUrlTemplate, subreddit, post.Id, post.Permalink)
  84. }
  85. forumPost := ForumPost{
  86. Title: html.UnescapeString(post.Title),
  87. DiscussionUrl: commentsUrl,
  88. TargetUrlDomain: post.Domain,
  89. CommentCount: post.CommentsCount,
  90. Score: post.Upvotes,
  91. TimePosted: time.Unix(int64(post.Time), 0),
  92. }
  93. if post.Thumbnail != "" && post.Thumbnail != "self" && post.Thumbnail != "default" && post.Thumbnail != "nsfw" {
  94. forumPost.ThumbnailUrl = html.UnescapeString(post.Thumbnail)
  95. }
  96. if !post.IsSelf {
  97. forumPost.TargetUrl = post.Url
  98. }
  99. if showFlairs && post.Flair != "" {
  100. forumPost.Tags = append(forumPost.Tags, post.Flair)
  101. }
  102. if len(post.ParentList) > 0 {
  103. forumPost.IsCrosspost = true
  104. forumPost.TargetUrlDomain = "r/" + post.ParentList[0].Subreddit
  105. if commentsUrlTemplate == "" {
  106. forumPost.TargetUrl = "https://www.reddit.com" + post.ParentList[0].Permalink
  107. } else {
  108. forumPost.TargetUrl = templateRedditCommentsURL(
  109. commentsUrlTemplate,
  110. post.ParentList[0].Subreddit,
  111. post.ParentList[0].Id,
  112. post.ParentList[0].Permalink,
  113. )
  114. }
  115. }
  116. posts = append(posts, forumPost)
  117. }
  118. return posts, nil
  119. }