reddit: added repository

This commit is contained in:
Tigor Hutasuhut 2024-04-10 17:13:07 +07:00
parent 1b4e0c7669
commit 8ec1175fe4
9 changed files with 496 additions and 4 deletions

View file

@ -3,6 +3,7 @@ package api
import ( import (
"context" "context"
"errors" "errors"
"net/http"
"github.com/tigorlazuardi/redmage/db/queries" "github.com/tigorlazuardi/redmage/db/queries"
"github.com/tigorlazuardi/redmage/pkg/errs" "github.com/tigorlazuardi/redmage/pkg/errs"
@ -12,16 +13,23 @@ type DownloadSubredditParams struct {
Countback int Countback int
NSFW bool NSFW bool
Devices []queries.Device Devices []queries.Device
Type int
} }
var ( var (
ErrNoDevices = errors.New("api: downloading subreddit images requires at least one device") ErrNoDevices = errors.New("api: no devices set")
ErrDownloadDirNotSet = errors.New("api: downloading subreddit images require download directory to be set") ErrDownloadDirNotSet = errors.New("api: download directory not set")
) )
func (api *API) DownloadSubredditImages(ctx context.Context, subredditName string, params DownloadSubredditParams) error { func (api *API) DownloadSubredditImages(ctx context.Context, subredditName string, params DownloadSubredditParams) error {
if len(params.Devices) == 0 { downloadDir := api.config.String("download.directory")
return errs.Wrap(ErrNoDevices) if downloadDir == "" {
return errs.Wrapw(ErrDownloadDirNotSet, "download directory must be set before images can be downloaded").Code(http.StatusBadRequest)
} }
if len(params.Devices) == 0 {
return errs.Wrapw(ErrNoDevices, "downloading images requires at least one device configured").Code(http.StatusBadRequest)
}
return nil return nil
} }

View file

@ -0,0 +1,16 @@
package api
import (
"context"
"github.com/tigorlazuardi/redmage/api/reddit"
)
type DownloadSubredditPostsParams struct {
Page int
Limit int
}
func (api *API) DownloadSubredditPosts(ctx context.Context, subredditName string, params DownloadSubredditParams) (posts []reddit.Post, err error) {
return posts, err
}

9
api/reddit/client.go Normal file
View file

@ -0,0 +1,9 @@
package reddit
import (
"net/http"
)
type Client interface {
Do(*http.Request) (*http.Response, error)
}

View file

@ -0,0 +1 @@
package reddit

64
api/reddit/get_posts.go Normal file
View file

@ -0,0 +1,64 @@
package reddit
import (
"context"
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"github.com/tigorlazuardi/redmage/pkg/errs"
)
type SubredditType int
const (
SubredditTypeSub SubredditType = iota
SubredditTypeUser
)
func (s SubredditType) Code() string {
switch s {
case SubredditTypeUser:
return "user"
default:
return "r"
}
}
type GetPostsParam struct {
Subreddit string
Limit int
Page int
SubredditType SubredditType
}
func (reddit *Reddit) GetPosts(ctx context.Context, params GetPostsParam) (posts []Post, err error) {
url := fmt.Sprintf("https://reddit.com/%s/%s.json?limit=%d&page=%d", params.SubredditType.Code(), params.Subreddit, params.Limit, params.Page)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
if err != nil {
return posts, errs.Wrapw(err, "reddit: failed to create http request instance", "url", url, "params", params)
}
res, err := reddit.Client.Do(req)
if err != nil {
return posts, errs.Wrapw(err, "reddit: failed to execute http request", "url", url, "params", params)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
body, _ := io.ReadAll(res.Body)
return posts, errs.Fail("reddit: unexpected status code when executing GetPosts",
slog.Group("request", "url", url, "params", params),
slog.Group("response", "status_code", res.StatusCode, "body", json.RawMessage(body)),
)
}
err = json.NewDecoder(res.Body).Decode(&posts)
if err != nil {
return posts, errs.Wrapw(err, "reddit: failed to parse response body when getting posts from reddit", "url", url, "params", params)
}
return posts, nil
}

191
api/reddit/post.go Normal file
View file

@ -0,0 +1,191 @@
package reddit
type Post struct {
Kind string `json:"kind"`
Data Data `json:"data"`
}
type (
MediaEmbed struct{}
SecureMediaEmbed struct{}
Gildings struct{}
Source struct {
URL string `json:"url"`
Width int `json:"width"`
Height int `json:"height"`
}
)
type Resolutions struct {
URL string `json:"url"`
Width int `json:"width"`
Height int `json:"height"`
}
type (
Variants struct{}
Images struct {
Source Source `json:"source"`
Resolutions []Resolutions `json:"resolutions"`
Variants Variants `json:"variants"`
ID string `json:"id"`
}
)
type Preview struct {
Images []Images `json:"images"`
Enabled bool `json:"enabled"`
}
type LinkFlairRichtext struct {
E string `json:"e"`
T string `json:"t"`
}
type ThumbnailPreview struct {
Y int `json:"y"`
X int `json:"x"`
U string `json:"u"`
}
type MediaMetadata struct {
Status string `json:"status"`
Kind string `json:"e"`
Mimetype string `json:"m"`
ExtraThumbnails []ThumbnailPreview `json:"p"`
Thumbnail ThumbnailPreview `json:"s"`
ID string `json:"id"`
}
type Items struct {
OutboundURL string `json:"outbound_url,omitempty"`
MediaID string `json:"media_id"`
ID int `json:"id"`
}
type GalleryData struct {
Items []Items `json:"items"`
}
type AuthorFlairRichtext struct {
E string `json:"e"`
T string `json:"t"`
}
type ChildrenData struct {
ApprovedAtUtc any `json:"approved_at_utc"`
Subreddit string `json:"subreddit"`
Selftext string `json:"selftext"`
AuthorFullname string `json:"author_fullname"`
Saved bool `json:"saved"`
ModReasonTitle any `json:"mod_reason_title"`
Gilded int `json:"gilded"`
Clicked bool `json:"clicked"`
IsGallery bool `json:"is_gallery"`
Title string `json:"title"`
LinkFlairRichtext []LinkFlairRichtext `json:"link_flair_richtext"`
SubredditNamePrefixed string `json:"subreddit_name_prefixed"`
Hidden bool `json:"hidden"`
Pwls int `json:"pwls"`
LinkFlairCSSClass string `json:"link_flair_css_class"`
Downs int `json:"downs"`
ThumbnailHeight int `json:"thumbnail_height"`
TopAwardedType any `json:"top_awarded_type"`
HideScore bool `json:"hide_score"`
MediaMetadata map[string]MediaMetadata `json:"media_metadata"`
Name string `json:"name"`
Quarantine bool `json:"quarantine"`
LinkFlairTextColor any `json:"link_flair_text_color"`
UpvoteRatio float64 `json:"upvote_ratio"`
AuthorFlairBackgroundColor any `json:"author_flair_background_color"`
Ups int `json:"ups"`
Domain string `json:"domain"`
MediaEmbed MediaEmbed `json:"media_embed"`
ThumbnailWidth int `json:"thumbnail_width"`
AuthorFlairTemplateID string `json:"author_flair_template_id"`
IsOriginalContent bool `json:"is_original_content"`
UserReports []any `json:"user_reports"`
SecureMedia any `json:"secure_media"`
IsRedditMediaDomain bool `json:"is_reddit_media_domain"`
IsMeta bool `json:"is_meta"`
Category any `json:"category"`
SecureMediaEmbed SecureMediaEmbed `json:"secure_media_embed"`
GalleryData GalleryData `json:"gallery_data"`
LinkFlairText string `json:"link_flair_text"`
CanModPost bool `json:"can_mod_post"`
Score int `json:"score"`
ApprovedBy any `json:"approved_by"`
IsCreatedFromAdsUI bool `json:"is_created_from_ads_ui"`
AuthorPremium bool `json:"author_premium"`
Thumbnail string `json:"thumbnail"`
Edited bool `json:"edited"`
AuthorFlairCSSClass string `json:"author_flair_css_class"`
AuthorFlairRichtext []AuthorFlairRichtext `json:"author_flair_richtext"`
Gildings Gildings `json:"gildings"`
ContentCategories any `json:"content_categories"`
IsSelf bool `json:"is_self"`
SubredditType string `json:"subreddit_type"`
Created int `json:"created"`
LinkFlairType string `json:"link_flair_type"`
Wls int `json:"wls"`
RemovedByCategory any `json:"removed_by_category"`
BannedBy any `json:"banned_by"`
AuthorFlairType string `json:"author_flair_type"`
TotalAwardsReceived int `json:"total_awards_received"`
AllowLiveComments bool `json:"allow_live_comments"`
SelftextHTML any `json:"selftext_html"`
Likes any `json:"likes"`
SuggestedSort any `json:"suggested_sort"`
BannedAtUtc any `json:"banned_at_utc"`
URLOverriddenByDest string `json:"url_overridden_by_dest"`
ViewCount any `json:"view_count"`
Archived bool `json:"archived"`
NoFollow bool `json:"no_follow"`
IsCrosspostable bool `json:"is_crosspostable"`
Pinned bool `json:"pinned"`
Over18 bool `json:"over_18"`
AllAwardings []any `json:"all_awardings"`
Awarders []any `json:"awarders"`
MediaOnly bool `json:"media_only"`
CanGild bool `json:"can_gild"`
Spoiler bool `json:"spoiler"`
Locked bool `json:"locked"`
AuthorFlairText string `json:"author_flair_text"`
TreatmentTags []any `json:"treatment_tags"`
Visited bool `json:"visited"`
RemovedBy any `json:"removed_by"`
ModNote any `json:"mod_note"`
Distinguished any `json:"distinguished"`
SubredditID string `json:"subreddit_id"`
AuthorIsBlocked bool `json:"author_is_blocked"`
ModReasonBy any `json:"mod_reason_by"`
NumReports any `json:"num_reports"`
RemovalReason any `json:"removal_reason"`
LinkFlairBackgroundColor any `json:"link_flair_background_color"`
ID string `json:"id"`
IsRobotIndexable bool `json:"is_robot_indexable"`
ReportReasons any `json:"report_reasons"`
Author string `json:"author"`
DiscussionType any `json:"discussion_type"`
NumComments int `json:"num_comments"`
SendReplies bool `json:"send_replies"`
WhitelistStatus string `json:"whitelist_status"`
ContestMode bool `json:"contest_mode"`
ModReports []any `json:"mod_reports"`
AuthorPatreonFlair bool `json:"author_patreon_flair"`
AuthorFlairTextColor string `json:"author_flair_text_color"`
Permalink string `json:"permalink"`
ParentWhitelistStatus string `json:"parent_whitelist_status"`
Stickied bool `json:"stickied"`
URL string `json:"url"`
SubredditSubscribers int `json:"subreddit_subscribers"`
CreatedUtc int `json:"created_utc"`
NumCrossposts int `json:"num_crossposts"`
Media any `json:"media"`
IsVideo bool `json:"is_video"`
}
type Children struct {
Kind string `json:"kind"`
Data ChildrenData `json:"data,omitempty"`
}
type Data struct {
After string `json:"after"`
Dist int `json:"dist"`
Modhash string `json:"modhash"`
GeoFilter any `json:"geo_filter"`
Children []Children `json:"children"`
Before any `json:"before"`
}

8
api/reddit/reddit.go Normal file
View file

@ -0,0 +1,8 @@
package reddit
import "github.com/tigorlazuardi/redmage/config"
type Reddit struct {
Client Client
Config *config.Config
}

View file

@ -0,0 +1,191 @@
package subredditpost
type SubredditPost struct {
Kind string `json:"kind"`
Data Data `json:"data"`
}
type (
MediaEmbed struct{}
SecureMediaEmbed struct{}
Gildings struct{}
Source struct {
URL string `json:"url"`
Width int `json:"width"`
Height int `json:"height"`
}
)
type Resolutions struct {
URL string `json:"url"`
Width int `json:"width"`
Height int `json:"height"`
}
type (
Variants struct{}
Images struct {
Source Source `json:"source"`
Resolutions []Resolutions `json:"resolutions"`
Variants Variants `json:"variants"`
ID string `json:"id"`
}
)
type Preview struct {
Images []Images `json:"images"`
Enabled bool `json:"enabled"`
}
type LinkFlairRichtext struct {
E string `json:"e"`
T string `json:"t"`
}
type ThumbnailPreview struct {
Y int `json:"y"`
X int `json:"x"`
U string `json:"u"`
}
type MediaMetadata struct {
Status string `json:"status"`
Kind string `json:"e"`
Mimetype string `json:"m"`
ExtraThumbnails []ThumbnailPreview `json:"p"`
Thumbnail ThumbnailPreview `json:"s"`
ID string `json:"id"`
}
type Items struct {
OutboundURL string `json:"outbound_url,omitempty"`
MediaID string `json:"media_id"`
ID int `json:"id"`
}
type GalleryData struct {
Items []Items `json:"items"`
}
type AuthorFlairRichtext struct {
E string `json:"e"`
T string `json:"t"`
}
type ChildrenData struct {
ApprovedAtUtc any `json:"approved_at_utc"`
Subreddit string `json:"subreddit"`
Selftext string `json:"selftext"`
AuthorFullname string `json:"author_fullname"`
Saved bool `json:"saved"`
ModReasonTitle any `json:"mod_reason_title"`
Gilded int `json:"gilded"`
Clicked bool `json:"clicked"`
IsGallery bool `json:"is_gallery"`
Title string `json:"title"`
LinkFlairRichtext []LinkFlairRichtext `json:"link_flair_richtext"`
SubredditNamePrefixed string `json:"subreddit_name_prefixed"`
Hidden bool `json:"hidden"`
Pwls int `json:"pwls"`
LinkFlairCSSClass string `json:"link_flair_css_class"`
Downs int `json:"downs"`
ThumbnailHeight int `json:"thumbnail_height"`
TopAwardedType any `json:"top_awarded_type"`
HideScore bool `json:"hide_score"`
MediaMetadata map[string]MediaMetadata `json:"media_metadata"`
Name string `json:"name"`
Quarantine bool `json:"quarantine"`
LinkFlairTextColor any `json:"link_flair_text_color"`
UpvoteRatio float64 `json:"upvote_ratio"`
AuthorFlairBackgroundColor any `json:"author_flair_background_color"`
Ups int `json:"ups"`
Domain string `json:"domain"`
MediaEmbed MediaEmbed `json:"media_embed"`
ThumbnailWidth int `json:"thumbnail_width"`
AuthorFlairTemplateID string `json:"author_flair_template_id"`
IsOriginalContent bool `json:"is_original_content"`
UserReports []any `json:"user_reports"`
SecureMedia any `json:"secure_media"`
IsRedditMediaDomain bool `json:"is_reddit_media_domain"`
IsMeta bool `json:"is_meta"`
Category any `json:"category"`
SecureMediaEmbed SecureMediaEmbed `json:"secure_media_embed"`
GalleryData GalleryData `json:"gallery_data"`
LinkFlairText string `json:"link_flair_text"`
CanModPost bool `json:"can_mod_post"`
Score int `json:"score"`
ApprovedBy any `json:"approved_by"`
IsCreatedFromAdsUI bool `json:"is_created_from_ads_ui"`
AuthorPremium bool `json:"author_premium"`
Thumbnail string `json:"thumbnail"`
Edited bool `json:"edited"`
AuthorFlairCSSClass string `json:"author_flair_css_class"`
AuthorFlairRichtext []AuthorFlairRichtext `json:"author_flair_richtext"`
Gildings Gildings `json:"gildings"`
ContentCategories any `json:"content_categories"`
IsSelf bool `json:"is_self"`
SubredditType string `json:"subreddit_type"`
Created int `json:"created"`
LinkFlairType string `json:"link_flair_type"`
Wls int `json:"wls"`
RemovedByCategory any `json:"removed_by_category"`
BannedBy any `json:"banned_by"`
AuthorFlairType string `json:"author_flair_type"`
TotalAwardsReceived int `json:"total_awards_received"`
AllowLiveComments bool `json:"allow_live_comments"`
SelftextHTML any `json:"selftext_html"`
Likes any `json:"likes"`
SuggestedSort any `json:"suggested_sort"`
BannedAtUtc any `json:"banned_at_utc"`
URLOverriddenByDest string `json:"url_overridden_by_dest"`
ViewCount any `json:"view_count"`
Archived bool `json:"archived"`
NoFollow bool `json:"no_follow"`
IsCrosspostable bool `json:"is_crosspostable"`
Pinned bool `json:"pinned"`
Over18 bool `json:"over_18"`
AllAwardings []any `json:"all_awardings"`
Awarders []any `json:"awarders"`
MediaOnly bool `json:"media_only"`
CanGild bool `json:"can_gild"`
Spoiler bool `json:"spoiler"`
Locked bool `json:"locked"`
AuthorFlairText string `json:"author_flair_text"`
TreatmentTags []any `json:"treatment_tags"`
Visited bool `json:"visited"`
RemovedBy any `json:"removed_by"`
ModNote any `json:"mod_note"`
Distinguished any `json:"distinguished"`
SubredditID string `json:"subreddit_id"`
AuthorIsBlocked bool `json:"author_is_blocked"`
ModReasonBy any `json:"mod_reason_by"`
NumReports any `json:"num_reports"`
RemovalReason any `json:"removal_reason"`
LinkFlairBackgroundColor any `json:"link_flair_background_color"`
ID string `json:"id"`
IsRobotIndexable bool `json:"is_robot_indexable"`
ReportReasons any `json:"report_reasons"`
Author string `json:"author"`
DiscussionType any `json:"discussion_type"`
NumComments int `json:"num_comments"`
SendReplies bool `json:"send_replies"`
WhitelistStatus string `json:"whitelist_status"`
ContestMode bool `json:"contest_mode"`
ModReports []any `json:"mod_reports"`
AuthorPatreonFlair bool `json:"author_patreon_flair"`
AuthorFlairTextColor string `json:"author_flair_text_color"`
Permalink string `json:"permalink"`
ParentWhitelistStatus string `json:"parent_whitelist_status"`
Stickied bool `json:"stickied"`
URL string `json:"url"`
SubredditSubscribers int `json:"subreddit_subscribers"`
CreatedUtc int `json:"created_utc"`
NumCrossposts int `json:"num_crossposts"`
Media any `json:"media"`
IsVideo bool `json:"is_video"`
}
type Children struct {
Kind string `json:"kind"`
Data ChildrenData `json:"data,omitempty"`
}
type Data struct {
After string `json:"after"`
Dist int `json:"dist"`
Modhash string `json:"modhash"`
GeoFilter any `json:"geo_filter"`
Children []Children `json:"children"`
Before any `json:"before"`
}

View file

@ -102,6 +102,10 @@ func (er *Err) Error() string {
return s.String() return s.String()
} }
func (er *Err) Unwrap() error {
return er.origin
}
func (er *Err) Message(msg string, args ...any) Error { func (er *Err) Message(msg string, args ...any) Error {
er.message = fmt.Sprintf(msg, args...) er.message = fmt.Sprintf(msg, args...)
return er return er