Adventure section: ingest, permalink, digest, emblems, noindex

Pete's side of the Adventure news feed. Receives structured game-event
facts from gogobee, templates them in Pete's warm-reporter voice, and
publishes to a new /adventure section + live Matrix posts.

- adventure.go: bearer ingest + fact-guard + 13 event templates;
  /adventure/{guid} permalink (story.html); per-event SVG emblems at
  /adventure/art/{type}.svg (card image + og:image); NoPush suppresses
  the live Matrix post (cold-start backfill).
- adventure_digest.go: daily BULLETIN roundup at DigestHour (UTC);
  unposted-in-48h = bulletins; marks them digested; per-day ?digest= URL
  avoids canonical dedup.
- config AdventureConfig (enabled/ingest_token/channel/digest_hour);
  web.New takes the seam + a priority poster; started in main.
- adventure theme colors; thumbURL passes through local emblem paths;
  adventure pages are noindex (player-named; gap #5).

Claude-Session: https://claude.ai/code/session_017mEwUmmS7aQTP2NQXj6rUa
This commit is contained in:
prosolis
2026-07-11 00:53:59 -07:00
parent 0a723418ff
commit 4c671fb410
17 changed files with 986 additions and 18 deletions

22
main.go
View File

@@ -258,14 +258,29 @@ func main() {
}
}
// Adapter: the web ingest handler posts priority adventure beats live to
// Matrix through the same queue everything else uses (bypasses pacing).
advPost := func(p web.AdvPost) {
queue.PostNow(poster.QueueItem{
GUID: p.GUID,
Headline: p.Headline,
Lede: p.Lede,
ImageURL: p.ImageURL,
ArticleURL: p.ArticleURL,
Source: p.Source,
Channel: p.Channel,
})
}
// Start the read-only web UI alongside the Matrix bot.
if cfg.Web.Enabled {
ws, err := web.New(cfg.Web, cfg.Sources, postingEnabled)
ws, err := web.New(cfg.Web, cfg.Sources, postingEnabled, cfg.Adventure, advPost)
if err != nil {
slog.Error("web server init failed", "err", err)
} else {
go ws.Start(ctx)
ws.StartPushSender(ctx)
ws.StartAdventureDigest(ctx)
}
}
@@ -370,8 +385,9 @@ func runLocal(cfg *config.Config) {
poller.Start(ctx)
slog.Info("local: pollers started")
// Local mode never connects to Matrix, so it's always web-only.
ws, err := web.New(cfg.Web, cfg.Sources, false)
// Local mode never connects to Matrix, so it's always web-only: adventure
// stories still ingest and render, but nothing posts (nil priority poster).
ws, err := web.New(cfg.Web, cfg.Sources, false, cfg.Adventure, nil)
if err != nil {
slog.Error("local: web server init failed", "err", err)
os.Exit(1)