prepare more reddit api
This commit is contained in:
parent
8ec1175fe4
commit
63f17437b7
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
"github.com/teivah/broadcast"
|
"github.com/teivah/broadcast"
|
||||||
|
"github.com/tigorlazuardi/redmage/api/bmessage"
|
||||||
"github.com/tigorlazuardi/redmage/config"
|
"github.com/tigorlazuardi/redmage/config"
|
||||||
"github.com/tigorlazuardi/redmage/db/queries"
|
"github.com/tigorlazuardi/redmage/db/queries"
|
||||||
"github.com/tigorlazuardi/redmage/pkg/errs"
|
"github.com/tigorlazuardi/redmage/pkg/errs"
|
||||||
|
@ -20,7 +21,7 @@ type API struct {
|
||||||
scheduler *cron.Cron
|
scheduler *cron.Cron
|
||||||
scheduleMap map[cron.EntryID]queries.Subreddit
|
scheduleMap map[cron.EntryID]queries.Subreddit
|
||||||
|
|
||||||
downloadBroadcast *broadcast.Relay[DownloadStatusMessage]
|
downloadBroadcast *broadcast.Relay[bmessage.DownloadStatusMessage]
|
||||||
|
|
||||||
config *config.Config
|
config *config.Config
|
||||||
}
|
}
|
||||||
|
@ -31,7 +32,7 @@ func New(q *queries.Queries, db *sql.DB, cfg *config.Config) *API {
|
||||||
db: db,
|
db: db,
|
||||||
scheduler: cron.New(),
|
scheduler: cron.New(),
|
||||||
scheduleMap: make(map[cron.EntryID]queries.Subreddit, 8),
|
scheduleMap: make(map[cron.EntryID]queries.Subreddit, 8),
|
||||||
downloadBroadcast: broadcast.NewRelay[DownloadStatusMessage](),
|
downloadBroadcast: broadcast.NewRelay[bmessage.DownloadStatusMessage](),
|
||||||
config: cfg,
|
config: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
api/bmessage/bmessage.go
Normal file
24
api/bmessage/bmessage.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package bmessage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alecthomas/units"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ImageMetadata struct {
|
||||||
|
URL string
|
||||||
|
Height int
|
||||||
|
Width int
|
||||||
|
ThumbnailURL string
|
||||||
|
ThumbnailHeight int
|
||||||
|
ThumbnailWidth int
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownloadStatusMessage struct {
|
||||||
|
Metadata ImageMetadata
|
||||||
|
ContantLength units.MetricBytes
|
||||||
|
Downloaded units.MetricBytes
|
||||||
|
Subreddit string
|
||||||
|
PostURL string
|
||||||
|
PostID string
|
||||||
|
Error error
|
||||||
|
}
|
|
@ -11,6 +11,6 @@ type DownloadSubredditPostsParams struct {
|
||||||
Limit int
|
Limit int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) DownloadSubredditPosts(ctx context.Context, subredditName string, params DownloadSubredditParams) (posts []reddit.Post, err error) {
|
func (api *API) DownloadSubredditPosts(ctx context.Context, subredditName string, params DownloadSubredditParams) (posts []reddit.Listing, err error) {
|
||||||
return posts, err
|
return posts, err
|
||||||
}
|
}
|
||||||
|
|
27
api/reddit/download_images.go
Normal file
27
api/reddit/download_images.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package reddit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/tigorlazuardi/redmage/api/bmessage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DownloadStatusBroadcaster interface {
|
||||||
|
Broadcast(bmessage.DownloadStatusMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullDownloadStatusBroadcaster struct{}
|
||||||
|
|
||||||
|
func (NullDownloadStatusBroadcaster) Broadcast(bmessage.DownloadStatusMessage) {}
|
||||||
|
|
||||||
|
type PostImage struct {
|
||||||
|
ImageURL string
|
||||||
|
ImageFile io.Reader
|
||||||
|
ThumbnailURL string
|
||||||
|
ThumbnailFile io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (reddit *Reddit) DownloadImage(ctx context.Context, post Post, broadcaster DownloadStatusBroadcaster) (images PostImage, err error) {
|
||||||
|
return images, err
|
||||||
|
}
|
|
@ -1 +1,39 @@
|
||||||
package reddit
|
package reddit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProgressReader struct {
|
||||||
|
OnProgress func(downloaded int64, contentLength int64, err error)
|
||||||
|
OnClose func(closeErr error)
|
||||||
|
|
||||||
|
reader io.ReadCloser
|
||||||
|
contentLength int64
|
||||||
|
downloaded int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (progressReader *ProgressReader) WrapHTTPResponse(resp *http.Response) *http.Response {
|
||||||
|
progressReader.reader = resp.Body
|
||||||
|
progressReader.contentLength = resp.ContentLength
|
||||||
|
resp.Body = progressReader
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (progressReader *ProgressReader) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = progressReader.reader.Read(p)
|
||||||
|
progressReader.downloaded += int64(n)
|
||||||
|
if progressReader.OnProgress != nil {
|
||||||
|
progressReader.OnProgress(progressReader.downloaded, progressReader.contentLength, err)
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (progressReader *ProgressReader) Close() error {
|
||||||
|
err := progressReader.reader.Close()
|
||||||
|
if progressReader.OnClose != nil {
|
||||||
|
progressReader.OnClose(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/tigorlazuardi/redmage/pkg/errs"
|
"github.com/tigorlazuardi/redmage/pkg/errs"
|
||||||
)
|
)
|
||||||
|
@ -34,7 +35,7 @@ type GetPostsParam struct {
|
||||||
SubredditType SubredditType
|
SubredditType SubredditType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (reddit *Reddit) GetPosts(ctx context.Context, params GetPostsParam) (posts []Post, err error) {
|
func (reddit *Reddit) GetPosts(ctx context.Context, params GetPostsParam) (posts Listing, err error) {
|
||||||
url := fmt.Sprintf("https://reddit.com/%s/%s.json?limit=%d&page=%d", params.SubredditType.Code(), params.Subreddit, params.Limit, params.Page)
|
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)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -51,7 +52,7 @@ func (reddit *Reddit) GetPosts(ctx context.Context, params GetPostsParam) (posts
|
||||||
body, _ := io.ReadAll(res.Body)
|
body, _ := io.ReadAll(res.Body)
|
||||||
return posts, errs.Fail("reddit: unexpected status code when executing GetPosts",
|
return posts, errs.Fail("reddit: unexpected status code when executing GetPosts",
|
||||||
slog.Group("request", "url", url, "params", params),
|
slog.Group("request", "url", url, "params", params),
|
||||||
slog.Group("response", "status_code", res.StatusCode, "body", json.RawMessage(body)),
|
slog.Group("response", "status_code", res.StatusCode, "body", formatLogBody(res, body)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,3 +63,10 @@ func (reddit *Reddit) GetPosts(ctx context.Context, params GetPostsParam) (posts
|
||||||
|
|
||||||
return posts, nil
|
return posts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatLogBody(res *http.Response, body []byte) any {
|
||||||
|
if strings.HasPrefix(res.Header.Get("Content-Type"), "application/json") {
|
||||||
|
return json.RawMessage(body)
|
||||||
|
}
|
||||||
|
return string(body)
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package reddit
|
package reddit
|
||||||
|
|
||||||
type Post struct {
|
type Listing struct {
|
||||||
Kind string `json:"kind"`
|
Kind string `json:"kind"`
|
||||||
Data Data `json:"data"`
|
Data Data `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Listing) GetPosts() []Post {
|
||||||
|
return l.Data.Children
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
MediaEmbed struct{}
|
MediaEmbed struct{}
|
||||||
SecureMediaEmbed struct{}
|
SecureMediaEmbed struct{}
|
||||||
|
@ -65,7 +69,7 @@ type AuthorFlairRichtext struct {
|
||||||
E string `json:"e"`
|
E string `json:"e"`
|
||||||
T string `json:"t"`
|
T string `json:"t"`
|
||||||
}
|
}
|
||||||
type ChildrenData struct {
|
type PostData struct {
|
||||||
ApprovedAtUtc any `json:"approved_at_utc"`
|
ApprovedAtUtc any `json:"approved_at_utc"`
|
||||||
Subreddit string `json:"subreddit"`
|
Subreddit string `json:"subreddit"`
|
||||||
Selftext string `json:"selftext"`
|
Selftext string `json:"selftext"`
|
||||||
|
@ -176,16 +180,44 @@ type ChildrenData struct {
|
||||||
NumCrossposts int `json:"num_crossposts"`
|
NumCrossposts int `json:"num_crossposts"`
|
||||||
Media any `json:"media"`
|
Media any `json:"media"`
|
||||||
IsVideo bool `json:"is_video"`
|
IsVideo bool `json:"is_video"`
|
||||||
|
PostHint string `json:"post_hint"`
|
||||||
|
Preview Preview `json:"preview"`
|
||||||
}
|
}
|
||||||
type Children struct {
|
|
||||||
Kind string `json:"kind"`
|
type Post struct {
|
||||||
Data ChildrenData `json:"data,omitempty"`
|
Kind string `json:"kind"`
|
||||||
|
Data PostData `json:"data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (post *Post) IsImagePost() bool {
|
||||||
|
return post.Data.PostHint == "image"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (post *Post) GetImageURL() string {
|
||||||
|
return post.Data.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (post *Post) GetImageSize() (height, width int) {
|
||||||
|
if len(post.Data.Preview.Images) == 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
source := post.Data.Preview.Images[0].Source
|
||||||
|
return source.Height, source.Width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (post *Post) GetThumbnailURL() string {
|
||||||
|
return post.Data.Thumbnail
|
||||||
|
}
|
||||||
|
|
||||||
|
func (post *Post) GetThumbnailSize() (height, width int) {
|
||||||
|
return post.Data.ThumbnailHeight, post.Data.ThumbnailWidth
|
||||||
|
}
|
||||||
|
|
||||||
type Data struct {
|
type Data struct {
|
||||||
After string `json:"after"`
|
After string `json:"after"`
|
||||||
Dist int `json:"dist"`
|
Dist int `json:"dist"`
|
||||||
Modhash string `json:"modhash"`
|
Modhash string `json:"modhash"`
|
||||||
GeoFilter any `json:"geo_filter"`
|
GeoFilter any `json:"geo_filter"`
|
||||||
Children []Children `json:"children"`
|
Children []Post `json:"children"`
|
||||||
Before any `json:"before"`
|
Before any `json:"before"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,191 +0,0 @@
|
||||||
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"`
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@ CREATE TABLE images(
|
||||||
title VARCHAR(255) NOT NULL,
|
title VARCHAR(255) NOT NULL,
|
||||||
post_id VARCHAR(50) NOT NULL,
|
post_id VARCHAR(50) NOT NULL,
|
||||||
post_url VARCHAR(255) NOT NULL,
|
post_url VARCHAR(255) NOT NULL,
|
||||||
|
post_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
poster VARCHAR(50) NOT NULL,
|
poster VARCHAR(50) NOT NULL,
|
||||||
poster_url VARCHAR(255) NOT NULL,
|
poster_url VARCHAR(255) NOT NULL,
|
||||||
image_relative_path VARCHAR(255) NOT NULL,
|
image_relative_path VARCHAR(255) NOT NULL,
|
||||||
|
|
7
go.mod
7
go.mod
|
@ -5,6 +5,7 @@ go 1.22.1
|
||||||
require (
|
require (
|
||||||
github.com/a-h/templ v0.2.648
|
github.com/a-h/templ v0.2.648
|
||||||
github.com/adrg/xdg v0.4.0
|
github.com/adrg/xdg v0.4.0
|
||||||
|
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9
|
||||||
github.com/fatih/color v1.16.0
|
github.com/fatih/color v1.16.0
|
||||||
github.com/go-chi/chi/v5 v5.0.12
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
|
@ -20,25 +21,23 @@ require (
|
||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/mattn/go-sqlite3 v1.14.6
|
github.com/mattn/go-sqlite3 v1.14.6
|
||||||
github.com/pressly/goose/v3 v3.19.2
|
github.com/pressly/goose/v3 v3.19.2
|
||||||
|
github.com/robfig/cron/v3 v3.0.0
|
||||||
github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551
|
github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
|
github.com/teivah/broadcast v0.1.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
|
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jonboulle/clockwork v0.4.0 // indirect
|
|
||||||
github.com/knadh/koanf/maps v0.1.1 // indirect
|
github.com/knadh/koanf/maps v0.1.1 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/mfridman/interpolate v0.0.2 // indirect
|
github.com/mfridman/interpolate v0.0.2 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/robfig/cron/v3 v3.0.0 // indirect
|
|
||||||
github.com/sethvargo/go-retry v0.2.4 // indirect
|
github.com/sethvargo/go-retry v0.2.4 // indirect
|
||||||
github.com/teivah/broadcast v0.1.0 // indirect
|
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect
|
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect
|
||||||
golang.org/x/sync v0.6.0 // indirect
|
golang.org/x/sync v0.6.0 // indirect
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -14,6 +14,8 @@ github.com/a-h/templ v0.2.648 h1:A1ggHGIE7AONOHrFaDTM8SrqgqHL6fWgWCijQ21Zy9I=
|
||||||
github.com/a-h/templ v0.2.648/go.mod h1:SA7mtYwVEajbIXFRh3vKdYm/4FYyLQAtPH1+KxzGPA8=
|
github.com/a-h/templ v0.2.648/go.mod h1:SA7mtYwVEajbIXFRh3vKdYm/4FYyLQAtPH1+KxzGPA8=
|
||||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||||
|
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
|
||||||
|
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk=
|
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk=
|
||||||
|
@ -47,8 +49,6 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-co-op/gocron/v2 v2.2.9 h1:aoKosYWSSdXFLecjFWX1i8+R6V7XdZb8sB2ZKAY5Yis=
|
|
||||||
github.com/go-co-op/gocron/v2 v2.2.9/go.mod h1:mZx3gMSlFnb97k3hRqX3+GdlG3+DUwTh6B8fnsTScXg=
|
|
||||||
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
|
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
|
||||||
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
|
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
|
||||||
github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI=
|
github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI=
|
||||||
|
@ -158,8 +158,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
|
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
|
||||||
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||||
|
@ -178,6 +176,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
@ -205,8 +204,6 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
|
|
||||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
|
||||||
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE=
|
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE=
|
||||||
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||||
|
@ -233,6 +230,7 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -67,7 +67,7 @@ func flatHeader(header http.Header) map[string]string {
|
||||||
m := make(map[string]string, len(header))
|
m := make(map[string]string, len(header))
|
||||||
|
|
||||||
for k := range header {
|
for k := range header {
|
||||||
m[k] = strings.Join(header[k], "; ")
|
m[k] = strings.Join(header[k], ", ")
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue