Fires 3x/day to Discord: a morning digest, then afternoon/evening reminders that only mention what's still outstanding (silent if everything's already done — no nagging for the sake of it). The morning ping also includes a live "All quests" snapshot for MS Rewards and Xbox (see below) — informational only, not tied to streaks.
ITEMS 2026-09-12). Quests rotate (daily/weekly/monthly)
rather than being a clean "done once a day" thing, so there was never a
real boolean to verify — checkMsRewardsXbox (still in
lib/checkers/msRewards.ts) always returned { done: null }, which
permanently stuck both this item and the combined "full day" streak at
zero. Rather than show a checklist line and a streak number that could
never move, it's just not in ITEMS any more. Xbox is still covered
properly — every current quest is listed in the morning "All quests"
snapshot (see below).A separate Discord message listing every current MS Rewards perk/activity and every current Xbox quest with its progress, e.g.:
Bing — Search: 1/1
Daily Set — Activity: 3/3
Play a PC game -- 0/1 (20 pts, Available)
Weekly PC bonus -- 1/5 (260 pts, Expired)
/earn, not
just the dashboard — no extra Kernel cost, just one more page navigation
in the same browser session). Scoped to exactly three sections from Earn,
per Connor's request (2026-09-12): Streaks, Explore on Bing, and
Keep earning — not the dashboard's "Your perks", nor Earn's "Level up
activities"/"Quests". Streaks are listed individually; Explore on
Bing/Keep earning are summarized as counts (mostly one-off quiz/article
cards, not meaningfully individual quests). See extractEarnPageLines in
lib/checkers/msRewards.ts to adjust scope again later.C:\dev\xbox-quest-check,
see below) that runs once each morning and reports in via quests.ts,
since the Xbox app can only be read from Connor's own machine.The Xbox app's quests have no web page and no public API (confirmed: checked xbox.com/Rewards, the signed-in xbox.com/play hub, site search, and actual network traffic — nothing). A full network-capture attempt with mitmproxy also came up empty even with loopback exemptions and both WinINET/WinHTTP proxy settings correctly configured — the app simply doesn't route through either proxy surface, not even a rejected connection attempt showed up.
What does work: the Xbox app's UI Automation tree (the same accessibility
API screen readers use) already contains everything as plain text, e.g.
"Weekly PC bonus, Play 5 days this week on PC for 15 minutes., 260 points, 1 out of 5 completed, Expired". C:\dev\xbox-quest-check\check.ps1 reads
that tree directly — no cookies, no auth, no network capture, just reading
what's already rendered on screen. Full details in that project's own
README.
Both Brilliant and MS Rewards Bing use Kernel (real
headless Chrome in the cloud) and both need their session cookie injected
via Puppeteer's page.setCookie(), NOT
page.setExtraHTTPHeaders({ Cookie: ... }).
This isn't a style preference — it was the difference between working and not. The debugging trail, for whenever this breaks again:
fetch() (no browser at all) genuinely can't see
MS Rewards' data from Val Town's IP, Kernel's IP, or Connor's own home
IP — always landed on the logged-out /about page.document.cookie (to catch httpOnly cookies), using
"Copy value" instead of drag-select, adding a full realistic Chrome
header set (Sec-Fetch-*, sec-ch-ua, etc.), forcing TLS 1.2. None of
it worked — every one of those attempts still used a raw Cookie header
override, whether via fetch() or Puppeteer's setExtraHTTPHeaders().page.setCookie() — letting Chrome's own cookie jar carry
the session instead of forcing a header — worked immediately, first
try, from Val Town's IP. Whatever Microsoft's edge was checking, it
cared about how the cookie arrived, not just its value or freshness.Lesson for future checkers on either of these sites (or similar
Microsoft/Google-style properties): always use setCookie(), never
setExtraHTTPHeaders for auth cookies.
✅ Already set.
👉 Add KERNEL_API_KEY here: https://www.val.town/x/CAS98/daily-streak-checklist/environment-variables?key=KERNEL_API_KEY
A real session credential — treat it like a password. It rides along in every request as proof you're logged in, and it expires periodically (you'll need to repeat this every so often when the check starts coming back "unverifiable").
rewards.bing.com, scroll to Request
Headers, right-click the Cookie row → Copy value (not
drag-select — long values wrap and drag-select can silently corrupt
them).👉 Add REWARDS_COOKIE here: https://www.val.town/x/CAS98/daily-streak-checklist/environment-variables?key=REWARDS_COOKIE
Same idea, same method (Network tab, not document.cookie — Brilliant's
auth cookie is httpOnly):
brilliant.org → Request Headers → Copy
value on Cookie.👉 Add BRILLIANT_COOKIE here: https://www.val.town/x/CAS98/daily-streak-checklist/environment-variables?key=BRILLIANT_COOKIE
Shared secret that quests.ts checks against, so only trusted local
scripts (currently just the Xbox quest reader) can write into
quest_snapshots. An earlier version of this README called it unused and
safe to delete — that was wrong and caused a real outage (the Xbox script
started getting 401s once it was deleted). It is used. If it ever needs
regenerating, update it in both places — here and in
C:\dev\xbox-quest-check's XBOX_REPORT_SECRET line in secrets.env — or
every local reporter script breaks silently until someone notices missing
quest data.
get_logs for this val —
usually either a cookie expiring (re-grab via the steps above) or the
site changing its markup, in which case the relevant file in
lib/checkers/ needs a look.morning.ts / afternoon.ts / evening.ts (via write_interval_settings)
if that bugs you.XboxQuestCheck, ~7:40am daily) on Connor's PC, which itself depends
on the Xbox app being launchable and the PC being on. If it's ever
missing from the morning ping, check
C:\dev\xbox-quest-check\check.log first.Add an entry to ITEMS in lib/config.ts — a key, displayName, and a
checker: () => Promise<CheckOutcome> — for something with a real daily
done/not-done state. No other file needs to change — streak tracking and
messaging pick it up automatically. But only add it if the checker can
sometimes return { done: true } — an item whose checker always returns
{ done: null } (nothing to verify, ever) will just sit there permanently
unfilled, exactly what happened with Xbox before it got pulled out. For
that kind of thing (no clean daily boolean, or genuinely unverifiable),
follow the quest-snapshot pattern below instead.
For a quest-list-style addition (rotating items, no clean daily
done/not-done) rather than a streak item, follow the Xbox pattern instead:
report into quest_snapshots via quests.ts (or compute inline if it can
run in Val Town) and add it to buildQuestMessage in lib/runCheck.ts.
morning.ts / afternoon.ts / evening.ts — the three cron entrypoints.quests.ts — HTTP endpoint for external scripts (currently just the
Xbox reader) to report a quest snapshot into quest_snapshots.lib/runCheck.ts — orchestration: runs checkers, updates streaks, builds
and sends the Discord messages (checklist + quest snapshot).lib/checkers/ — one file per verified streak item.lib/db.ts — SQLite-backed state: items/daily_checks (streaks) and
quest_snapshots (live quest lists).lib/discord.ts — webhook sender.lib/config.ts — the list of tracked streak items + the CheckOutcome type.C:\dev\xbox-quest-check (local, not in this val) — the Xbox UI
Automation reader + its own Scheduled Task and README.Use run_file on morning.ts (or afternoon.ts/evening.ts) to trigger a
check manually instead of waiting for the schedule, then check get_logs
for any checker warnings. To test the Xbox side, run
C:\dev\xbox-quest-check\check.ps1 directly and check its check.log.