Adventure news: fix posting gates, digest counts, and section gating
- Don't post adventure beats when posting.enabled=false (PostNow ignores the flag) - Keep the adventure channel out of the round-robin rotation so it can't steal bulletins from the digest - no_push now retires a fact against the digest, not just the live post - Default a missing occurred_at to now instead of the Unix epoch - Keep protocol-relative image URLs on the guarded thumbnailer - Make digest_hour=0 (midnight UTC) settable - Quote the true window total in the digest, not the truncated slice - Hide the Adventure section entirely when it's disabled
This commit is contained in:
@@ -44,9 +44,10 @@ func (s *Server) StartAdventureDigest(ctx context.Context) {
|
||||
// 24h. Sleeping to a wall-clock hour (not a fixed ticker from boot) keeps the
|
||||
// digest at a predictable time of day across restarts.
|
||||
func (s *Server) runAdventureDigest(ctx context.Context) {
|
||||
slog.Info("web: adventure digest scheduler started", "digest_hour_utc", s.adv.DigestHour, "channel", s.adv.Channel)
|
||||
hour := s.adv.DigestHourOrDefault()
|
||||
slog.Info("web: adventure digest scheduler started", "digest_hour_utc", hour, "channel", s.adv.Channel)
|
||||
for {
|
||||
wait := durUntilNextHour(time.Now().UTC(), s.adv.DigestHour)
|
||||
wait := durUntilNextHour(time.Now().UTC(), hour)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
@@ -72,7 +73,7 @@ func durUntilNextHour(now time.Time, hour int) time.Duration {
|
||||
// a dormant realm should stay quiet, not ship an empty digest.
|
||||
func (s *Server) postDailyDigest(now time.Time) {
|
||||
since := now.Add(-digestWindow).Unix()
|
||||
bulletins, err := storage.UnpostedAdventureSince(since, digestCap+1)
|
||||
bulletins, total, err := storage.UnpostedAdventureSince(since, digestCap)
|
||||
if err != nil {
|
||||
slog.Error("adventure digest: query failed", "err", err)
|
||||
return
|
||||
@@ -80,16 +81,13 @@ func (s *Server) postDailyDigest(now time.Time) {
|
||||
if len(bulletins) == 0 {
|
||||
return
|
||||
}
|
||||
truncated := false
|
||||
if len(bulletins) > digestCap {
|
||||
bulletins = bulletins[:digestCap]
|
||||
truncated = true
|
||||
slog.Warn("adventure digest: window exceeded cap, truncating", "cap", digestCap)
|
||||
if total > len(bulletins) {
|
||||
slog.Warn("adventure digest: window exceeded cap, truncating", "cap", digestCap, "total", total)
|
||||
}
|
||||
|
||||
date := now.Format("2006-01-02")
|
||||
eventID := "adv-digest:" + date
|
||||
headline, lede := buildDigest(bulletins, truncated)
|
||||
headline, lede := buildDigest(bulletins, total)
|
||||
|
||||
s.advPost(AdvPost{
|
||||
GUID: eventID,
|
||||
@@ -109,15 +107,16 @@ func (s *Server) postDailyDigest(now time.Time) {
|
||||
}
|
||||
|
||||
// buildDigest renders the roundup headline + lede in Pete's warm reporter voice.
|
||||
// Headlines come from stored, fact-guarded rows, so no player-controlled text is
|
||||
// introduced here.
|
||||
func buildDigest(bulletins []storage.Story, truncated bool) (headline, lede string) {
|
||||
n := len(bulletins)
|
||||
if n == 1 {
|
||||
// total is how many bulletins the window actually holds, which is larger than
|
||||
// len(bulletins) when the cap truncated the fetch — the counts quoted to readers
|
||||
// have to describe the realm, not the slice. Headlines come from stored,
|
||||
// fact-guarded rows, so no player-controlled text is introduced here.
|
||||
func buildDigest(bulletins []storage.Story, total int) (headline, lede string) {
|
||||
if total == 1 {
|
||||
return "Today in the realm: one dispatch.",
|
||||
fmt.Sprintf("Quiet day out there, but one worth noting — %s Full story on the board.", trimHeadline(bulletins[0].Headline))
|
||||
}
|
||||
headline = fmt.Sprintf("Today in the realm: %d dispatches.", n)
|
||||
headline = fmt.Sprintf("Today in the realm: %d dispatches.", total)
|
||||
|
||||
shown := bulletins
|
||||
if len(shown) > digestPreview {
|
||||
@@ -129,8 +128,8 @@ func buildDigest(bulletins []storage.Story, truncated bool) (headline, lede stri
|
||||
}
|
||||
list := strings.Join(parts, " ")
|
||||
more := ""
|
||||
if n > len(shown) || truncated {
|
||||
more = fmt.Sprintf(" …and %d more.", n-len(shown))
|
||||
if total > len(shown) {
|
||||
more = fmt.Sprintf(" …and %d more.", total-len(shown))
|
||||
}
|
||||
return headline, fmt.Sprintf("Here's what came across the wire today. %s%s The full board's on the site.", list, more)
|
||||
}
|
||||
@@ -152,6 +151,5 @@ func trimHeadline(h string) string {
|
||||
// canonical-URL dedup treats each day's digest as distinct (it strips only
|
||||
// tracking params, keeping ?digest=).
|
||||
func (s *Server) digestURL(date string) string {
|
||||
base := strings.TrimRight(s.cfg.BaseURL, "/")
|
||||
return base + "/adventure?digest=" + date
|
||||
return s.siteURL("/adventure?digest=" + date)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user