Documentation
¶
Overview ¶
Package reddit provides functionality to fetch goal replay links from r/soccer.
Index ¶
- Constants
- Variables
- func IsNotFound(link *GoalLink) bool
- type Client
- type DebugLogger
- type Fetcher
- type GoalInfo
- type GoalLink
- type GoalLinkCache
- func (c *GoalLinkCache) All(matchID int) []GoalLink
- func (c *GoalLinkCache) CleanExpired() error
- func (c *GoalLinkCache) Clear() error
- func (c *GoalLinkCache) Get(key GoalLinkKey) *GoalLink
- func (c *GoalLinkCache) Set(link GoalLink) error
- func (c *GoalLinkCache) SetNotFound(matchID, minute int) error
- func (c *GoalLinkCache) Size() int
- type GoalLinkKey
- type GoalResult
- type MatchConfidence
- type PublicJSONFetcher
- type SearchResult
Constants ¶
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__" )
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 ¶
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 ¶
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 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 ¶
ClearCache clears the goal link cache.
func (*Client) DebugLog ¶ added in v0.28.0
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 (*Client) GoalLink ¶ added in v0.11.0
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 ¶
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 ¶
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").