Restructure & refactor codebase
This commit is contained in:
263
internal/glance/widget-reddit.go
Normal file
263
internal/glance/widget-reddit.go
Normal file
@@ -0,0 +1,263 @@
|
||||
package glance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
redditWidgetHorizontalCardsTemplate = mustParseTemplate("reddit-horizontal-cards.html", "widget-base.html")
|
||||
redditWidgetVerticalCardsTemplate = mustParseTemplate("reddit-vertical-cards.html", "widget-base.html")
|
||||
)
|
||||
|
||||
type redditWidget struct {
|
||||
widgetBase `yaml:",inline"`
|
||||
Posts forumPostList `yaml:"-"`
|
||||
Subreddit string `yaml:"subreddit"`
|
||||
Style string `yaml:"style"`
|
||||
ShowThumbnails bool `yaml:"show-thumbnails"`
|
||||
ShowFlairs bool `yaml:"show-flairs"`
|
||||
SortBy string `yaml:"sort-by"`
|
||||
TopPeriod string `yaml:"top-period"`
|
||||
Search string `yaml:"search"`
|
||||
ExtraSortBy string `yaml:"extra-sort-by"`
|
||||
CommentsUrlTemplate string `yaml:"comments-url-template"`
|
||||
Limit int `yaml:"limit"`
|
||||
CollapseAfter int `yaml:"collapse-after"`
|
||||
RequestUrlTemplate string `yaml:"request-url-template"`
|
||||
}
|
||||
|
||||
func (widget *redditWidget) initialize() error {
|
||||
if widget.Subreddit == "" {
|
||||
return errors.New("subreddit is required")
|
||||
}
|
||||
|
||||
if widget.Limit <= 0 {
|
||||
widget.Limit = 15
|
||||
}
|
||||
|
||||
if widget.CollapseAfter == 0 || widget.CollapseAfter < -1 {
|
||||
widget.CollapseAfter = 5
|
||||
}
|
||||
|
||||
if !isValidRedditSortType(widget.SortBy) {
|
||||
widget.SortBy = "hot"
|
||||
}
|
||||
|
||||
if !isValidRedditTopPeriod(widget.TopPeriod) {
|
||||
widget.TopPeriod = "day"
|
||||
}
|
||||
|
||||
if widget.RequestUrlTemplate != "" {
|
||||
if !strings.Contains(widget.RequestUrlTemplate, "{REQUEST-URL}") {
|
||||
return errors.New("no `{REQUEST-URL}` placeholder specified")
|
||||
}
|
||||
}
|
||||
|
||||
widget.
|
||||
withTitle("/r/" + widget.Subreddit).
|
||||
withTitleURL("https://www.reddit.com/r/" + widget.Subreddit + "/").
|
||||
withCacheDuration(30 * time.Minute)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isValidRedditSortType(sortBy string) bool {
|
||||
return sortBy == "hot" ||
|
||||
sortBy == "new" ||
|
||||
sortBy == "top" ||
|
||||
sortBy == "rising"
|
||||
}
|
||||
|
||||
func isValidRedditTopPeriod(period string) bool {
|
||||
return period == "hour" ||
|
||||
period == "day" ||
|
||||
period == "week" ||
|
||||
period == "month" ||
|
||||
period == "year" ||
|
||||
period == "all"
|
||||
}
|
||||
|
||||
func (widget *redditWidget) update(ctx context.Context) {
|
||||
// TODO: refactor, use a struct to pass all of these
|
||||
posts, err := fetchSubredditPosts(
|
||||
widget.Subreddit,
|
||||
widget.SortBy,
|
||||
widget.TopPeriod,
|
||||
widget.Search,
|
||||
widget.CommentsUrlTemplate,
|
||||
widget.RequestUrlTemplate,
|
||||
widget.ShowFlairs,
|
||||
)
|
||||
|
||||
if !widget.canContinueUpdateAfterHandlingErr(err) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(posts) > widget.Limit {
|
||||
posts = posts[:widget.Limit]
|
||||
}
|
||||
|
||||
if widget.ExtraSortBy == "engagement" {
|
||||
posts.calculateEngagement()
|
||||
posts.sortByEngagement()
|
||||
}
|
||||
|
||||
widget.Posts = posts
|
||||
}
|
||||
|
||||
func (widget *redditWidget) Render() template.HTML {
|
||||
if widget.Style == "horizontal-cards" {
|
||||
return widget.renderTemplate(widget, redditWidgetHorizontalCardsTemplate)
|
||||
}
|
||||
|
||||
if widget.Style == "vertical-cards" {
|
||||
return widget.renderTemplate(widget, redditWidgetVerticalCardsTemplate)
|
||||
}
|
||||
|
||||
return widget.renderTemplate(widget, forumPostsTemplate)
|
||||
|
||||
}
|
||||
|
||||
type subredditResponseJson struct {
|
||||
Data struct {
|
||||
Children []struct {
|
||||
Data struct {
|
||||
Id string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Upvotes int `json:"ups"`
|
||||
Url string `json:"url"`
|
||||
Time float64 `json:"created"`
|
||||
CommentsCount int `json:"num_comments"`
|
||||
Domain string `json:"domain"`
|
||||
Permalink string `json:"permalink"`
|
||||
Stickied bool `json:"stickied"`
|
||||
Pinned bool `json:"pinned"`
|
||||
IsSelf bool `json:"is_self"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Flair string `json:"link_flair_text"`
|
||||
ParentList []struct {
|
||||
Id string `json:"id"`
|
||||
Subreddit string `json:"subreddit"`
|
||||
Permalink string `json:"permalink"`
|
||||
} `json:"crosspost_parent_list"`
|
||||
} `json:"data"`
|
||||
} `json:"children"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func templateRedditCommentsURL(template, subreddit, postId, postPath string) string {
|
||||
template = strings.ReplaceAll(template, "{SUBREDDIT}", subreddit)
|
||||
template = strings.ReplaceAll(template, "{POST-ID}", postId)
|
||||
template = strings.ReplaceAll(template, "{POST-PATH}", strings.TrimLeft(postPath, "/"))
|
||||
|
||||
return template
|
||||
}
|
||||
|
||||
func fetchSubredditPosts(subreddit, sort, topPeriod, search, commentsUrlTemplate, requestUrlTemplate string, showFlairs bool) (forumPostList, error) {
|
||||
query := url.Values{}
|
||||
var requestUrl string
|
||||
|
||||
if search != "" {
|
||||
query.Set("q", search+" subreddit:"+subreddit)
|
||||
query.Set("sort", sort)
|
||||
}
|
||||
|
||||
if sort == "top" {
|
||||
query.Set("t", topPeriod)
|
||||
}
|
||||
|
||||
if search != "" {
|
||||
requestUrl = fmt.Sprintf("https://www.reddit.com/search.json?%s", query.Encode())
|
||||
} else {
|
||||
requestUrl = fmt.Sprintf("https://www.reddit.com/r/%s/%s.json?%s", subreddit, sort, query.Encode())
|
||||
}
|
||||
|
||||
if requestUrlTemplate != "" {
|
||||
requestUrl = strings.ReplaceAll(requestUrlTemplate, "{REQUEST-URL}", requestUrl)
|
||||
}
|
||||
|
||||
request, err := http.NewRequest("GET", requestUrl, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Required to increase rate limit, otherwise Reddit randomly returns 429 even after just 2 requests
|
||||
setBrowserUserAgentHeader(request)
|
||||
responseJson, err := decodeJsonFromRequest[subredditResponseJson](defaultClient, request)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(responseJson.Data.Children) == 0 {
|
||||
return nil, fmt.Errorf("no posts found")
|
||||
}
|
||||
|
||||
posts := make(forumPostList, 0, len(responseJson.Data.Children))
|
||||
|
||||
for i := range responseJson.Data.Children {
|
||||
post := &responseJson.Data.Children[i].Data
|
||||
|
||||
if post.Stickied || post.Pinned {
|
||||
continue
|
||||
}
|
||||
|
||||
var commentsUrl string
|
||||
|
||||
if commentsUrlTemplate == "" {
|
||||
commentsUrl = "https://www.reddit.com" + post.Permalink
|
||||
} else {
|
||||
commentsUrl = templateRedditCommentsURL(commentsUrlTemplate, subreddit, post.Id, post.Permalink)
|
||||
}
|
||||
|
||||
forumPost := forumPost{
|
||||
Title: html.UnescapeString(post.Title),
|
||||
DiscussionUrl: commentsUrl,
|
||||
TargetUrlDomain: post.Domain,
|
||||
CommentCount: post.CommentsCount,
|
||||
Score: post.Upvotes,
|
||||
TimePosted: time.Unix(int64(post.Time), 0),
|
||||
}
|
||||
|
||||
if post.Thumbnail != "" && post.Thumbnail != "self" && post.Thumbnail != "default" {
|
||||
forumPost.ThumbnailUrl = post.Thumbnail
|
||||
}
|
||||
|
||||
if !post.IsSelf {
|
||||
forumPost.TargetUrl = post.Url
|
||||
}
|
||||
|
||||
if showFlairs && post.Flair != "" {
|
||||
forumPost.Tags = append(forumPost.Tags, post.Flair)
|
||||
}
|
||||
|
||||
if len(post.ParentList) > 0 {
|
||||
forumPost.IsCrosspost = true
|
||||
forumPost.TargetUrlDomain = "r/" + post.ParentList[0].Subreddit
|
||||
|
||||
if commentsUrlTemplate == "" {
|
||||
forumPost.TargetUrl = "https://www.reddit.com" + post.ParentList[0].Permalink
|
||||
} else {
|
||||
forumPost.TargetUrl = templateRedditCommentsURL(
|
||||
commentsUrlTemplate,
|
||||
post.ParentList[0].Subreddit,
|
||||
post.ParentList[0].Id,
|
||||
post.ParentList[0].Permalink,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
posts = append(posts, forumPost)
|
||||
}
|
||||
|
||||
return posts, nil
|
||||
}
|
||||
Reference in New Issue
Block a user