reddit

package
v0.33.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package reddit provides functionality to fetch goal replay links from r/soccer.

Index

Constants

View Source
const (

	// CacheTTL defines how long goal links are stored.
	// 7 days keeps the cache file small while covering recent matches.
	CacheTTL = 7 * 24 * time.Hour // 7 days
	// NotFoundTTL defines how long to cache "not found" results.
	// Shorter than CacheTTL since links might appear later. 1h is a balance:
	// long enough to avoid re-hitting Reddit for the same missing goal within
	// a single viewing session (queue is paced at 30s/request), short enough
	// that late-uploaded videos surface on the next app run.
	NotFoundTTL = 1 * time.Hour
	// NotFoundMarker is a special URL indicating "searched but not found"
	NotFoundMarker = "__NOT_FOUND__"
)
View Source
const (
	// QueueInterval is the minimum gap between successive fetch attempts the
	// queue worker will perform against Reddit. 30s is conservative; bursts
	// against Reddit's edge are the failure mode this queue exists to avoid.
	QueueInterval = 30 * time.Second

	// CooldownPeriod is the base cooldown the queue pauses fetching for after
	// an ErrBlocked response from Reddit. Repeated blocks grow this
	// exponentially (see nextCooldown) up to maxBackoffMultiplier, since a
	// single fixed window isn't reliably enough to clear an IP-level block
	// that recurs shortly after the previous cooldown elapsed.
	CooldownPeriod = 10 * time.Minute
)

Defaults for the goal-link queue. Exposed as variables (not constants) so the queue type can override them in tests without test-only flags in production code paths.

Variables

View Source
var ErrBlocked = errors.New("reddit: blocked (HTTP 403)")

ErrBlocked indicates Reddit's edge returned an HTTP 403 (typically the "blocked by network security" interstitial). Returned by Search so callers can react with errors.Is without sniffing HTML response bodies.

Functions

func IsNotFound

func IsNotFound(link *GoalLink) bool

IsNotFound returns true if the cached entry is a "not found" marker.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client provides goal replay link fetching from Reddit r/soccer. Uses Reddit's public JSON API for goal link retrieval.

func NewClient

func NewClient() (*Client, error)

NewClient creates a new Reddit client with the default public JSON fetcher.

func NewClientWithDebug added in v0.12.0

func NewClientWithDebug(debugLogger DebugLogger) (*Client, error)

NewClientWithDebug creates a new Reddit client with debug logging enabled. Uses public JSON API like main branch.

func NewClientWithFetcher

func NewClientWithFetcher(fetcher Fetcher, cache *GoalLinkCache) *Client

NewClientWithFetcher creates a new Reddit client with a custom fetcher. Use this for testing with custom fetchers.

func (*Client) Cache

func (c *Client) Cache() *GoalLinkCache

Cache returns the underlying cache for direct access if needed.

func (*Client) ClearCache

func (c *Client) ClearCache() error

ClearCache clears the goal link cache.

func (*Client) DebugLog added in v0.28.0

func (c *Client) DebugLog(message string)

DebugLog forwards a message to the configured debug logger if one is wired. No-op in non-debug runs. Exported so callers outside the reddit package (e.g., the app's goal-link orchestration) emit their goal-related diagnostics through the same logger as the reddit client's internal search logs.

func (c *Client) GoalLink(goal GoalInfo) (*GoalLink, error)

GoalLink retrieves a cached goal link or fetches from Reddit if not cached. Returns nil if the goal link was previously searched but not found.

func (*Client) GoalLinksAsync added in v0.29.0

func (c *Client) GoalLinksAsync(goals []GoalInfo) <-chan GoalResult

GoalLinksAsync schedules a fetch for each goal through the per-Client queue and streams results on the returned channel. Cache hits are emitted immediately; uncached goals are enqueued for serial fetching at QueueInterval pacing. The returned channel is closed once every goal in `goals` has produced a result (or been deduplicated against an in-flight peer). Each emitted GoalResult.Link is nil when the goal was not found, was dropped due to an ErrBlocked cooldown, or hit a transient fetch error.

Use this in preference to the synchronous GoalLinks: it's what the app's subscription wiring consumes for progressive per-goal UI updates and is the only path that honors the global queue's cooldown semantics.

type DebugLogger added in v0.12.0

type DebugLogger func(message string)

DebugLogger is a function type for debug logging

type Fetcher

type Fetcher interface {
	Search(query string, limit int, matchTime time.Time, sort string) ([]SearchResult, error)
}

Fetcher defines the interface for fetching data from Reddit. Uses Reddit's public JSON API for goal link retrieval.

type GoalInfo

type GoalInfo struct {
	MatchID       int
	HomeTeam      string
	AwayTeam      string
	HomeTeamShort string // Short/alternative name (e.g., "Wolves", "Man Utd")
	AwayTeamShort string // Short/alternative name (e.g., "Wolves", "Man Utd")
	ScorerName    string
	Minute        int
	DisplayMinute string // e.g., "45+2'" for stoppage time display
	HomeScore     int
	AwayScore     int
	IsHomeTeam    bool
	MatchTime     time.Time
}

GoalInfo contains information about a goal to search for.

type GoalLink struct {
	MatchID   int       `json:"match_id"`
	Minute    int       `json:"minute"`
	URL       string    `json:"url"`
	Title     string    `json:"title"`
	PostURL   string    `json:"post_url"`
	FetchedAt time.Time `json:"fetched_at"`
}

GoalLink represents a cached goal replay link from Reddit.

type GoalLinkCache

type GoalLinkCache struct {
	// contains filtered or unexported fields
}

GoalLinkCache provides persistent storage for goal replay links.

func NewGoalLinkCache

func NewGoalLinkCache() (*GoalLinkCache, error)

NewGoalLinkCache creates a new cache, loading existing data from disk.

func (*GoalLinkCache) All added in v0.11.0

func (c *GoalLinkCache) All(matchID int) []GoalLink

All returns all cached goal links for a match.

func (*GoalLinkCache) CleanExpired

func (c *GoalLinkCache) CleanExpired() error

CleanExpired removes expired entries from the cache. Uses different TTLs for regular links vs "not found" markers.

func (*GoalLinkCache) Clear

func (c *GoalLinkCache) Clear() error

Clear removes all cached goal links.

func (*GoalLinkCache) Get

func (c *GoalLinkCache) Get(key GoalLinkKey) *GoalLink

Get retrieves a goal link from cache if it exists and is not expired. Returns nil if not cached or expired. To distinguish "not found" from "not cached", use Exists().

func (*GoalLinkCache) Set

func (c *GoalLinkCache) Set(link GoalLink) error

Set stores a goal link in the cache and persists to disk.

func (*GoalLinkCache) SetNotFound

func (c *GoalLinkCache) SetNotFound(matchID, minute int) error

SetNotFound stores a "not found" marker in the cache. This prevents re-fetching goals that weren't found on Reddit.

func (*GoalLinkCache) Size

func (c *GoalLinkCache) Size() int

Size returns the number of cached goal links.

type GoalLinkKey

type GoalLinkKey struct {
	MatchID int
	Minute  int
}

GoalLinkKey creates a unique key for a goal (matchID + minute).

type GoalResult added in v0.29.0

type GoalResult struct {
	Key  GoalLinkKey
	Link *GoalLink
}

GoalResult is the per-goal outcome streamed from Client.GoalLinksAsync. Link is nil when the goal was searched but no match was found, or when the fetch was dropped due to a Reddit block (the queue does not cache blocks, so a future call will re-try after the cooldown elapses).

type MatchConfidence

type MatchConfidence int

MatchConfidence represents how confident we are in a match.

const (
	ConfidenceNone   MatchConfidence = 0
	ConfidenceLow    MatchConfidence = 1
	ConfidenceMedium MatchConfidence = 2
	ConfidenceHigh   MatchConfidence = 3
)

func CalculateConfidence

func CalculateConfidence(result SearchResult, goal GoalInfo) MatchConfidence

CalculateConfidence returns the confidence level for a match.

type PublicJSONFetcher

type PublicJSONFetcher struct {
	// contains filtered or unexported fields
}

PublicJSONFetcher uses Reddit's public JSON endpoints (no auth required). Uses Reddit's public JSON API with rate limiting.

func NewPublicJSONFetcher

func NewPublicJSONFetcher() *PublicJSONFetcher

NewPublicJSONFetcher creates a new fetcher using public Reddit JSON API.

func (*PublicJSONFetcher) Search

func (f *PublicJSONFetcher) Search(query string, limit int, matchTime time.Time, sort string) ([]SearchResult, error)

Search performs a search on r/soccer for Media posts matching the query. matchTime is used to filter results to posts created around the match date. sort controls the result ordering (e.g., "relevance", "top", "new", "hot").

type SearchResult

type SearchResult struct {
	Title     string
	URL       string // The media URL (video/gif link)
	PostURL   string // The Reddit post URL
	Flair     string // e.g., "Media"
	CreatedAt time.Time
	Score     int
}

SearchResult represents a Reddit search result from r/soccer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL