No updates detected yet. GitHub releases will appear here.
Loading the full README. The saved excerpt is shown below.
One feed for New York nightlife. NOCT pulls club nights, raves and day parties from every platform that
lists them — Resident Advisor, DICE, venue calendars, Ticketmaster (and EDMTrain if licensed) — dedupes
them into one event per night per room, shows every ticket price side by side, and tags each night with a
genre and a vibe.
NOCT is a working name. Nothing is attached to it yet.Read the full README ↗One feed for New York nightlife. NOCT pulls club nights, raves and day parties from every platform that lists them — Resident Advisor, DICE, venue calendars, Ticketmaster (and EDMTrain if licensed) — dedupes them into one event per night per room, shows every ticket price side by side, and tags each night with a genre and a vibe.
NOCT is a working name. Nothing is attached to it yet.
index.html the app (mobile-first, 440px). Loads /api/feed; falls back to the sample weekend
studio/ the Instagram review page, served gated by api/studio.ts (never as a static file)
api/ Vercel Functions
feed.ts GET /api/feed?from&to → events/venues/days in the shape the UI expects
(&counts=1 → per-night counts only, what the month calendar draws)
recommend.ts GET /api/recommend → nights picked from the caller's own going/saved history
ingest/[source].ts GET|POST /api/ingest/ra|dice|elsewhere|…|all (Bearer CRON_SECRET)
enrich.ts POST /api/enrich — genre/vibe pass over new/changed events
health.ts GET /api/health — last run per source
studio.ts GET /studio — the Instagram review page, behind a studio session
session.ts POST|DELETE /api/session — sign in to the studio, or out
posts.ts the queue's data API: draft a weekend, edit a caption, approve or pass
img.ts GET /api/img — one promoter flyer, treated, as JPEG (host allowlist)
render.ts GET composes one 1080x1350 slide from a signed URL; POST signs a deck
publish.ts POST /api/publish — puts one approved deck on Instagram. Manual, never cron
GET — preflight: does the credential work, how many posts left today
src/
sources/ one adapter per source (RA, DICE, Elsewhere, Good Room, Public Records, SILO, Ticketmaster, EDMTrain): fetch + normalise, no DB
ingest/ runner: adapter → upsert_listing() → resolve_pending() → tombstone_sweep()
enrich/ taxonomy, crosswalk, rules engine, Claude reconciler
feed/ read model → UI shape, and the recommendation scorer's caller
post/ Instagram: slide templates (satori), tones/veil/grain (sharp), flyer treatments,
weekend drafting, caption house rules, the queue store, the Instagram API
client, and the 60-day token refresh that keeps a weekly post unattended
lib/ http (polite fetch, block detection), time (NY ⇄ UTC), normalize, db
supabase/migrations/ Postgres schema: listings, events, venues/aliases, resolution SQL, enrichment,
app tables, views + RLS, pg_cron schedules
tests/ vitest — unit tests on real captured payloads; DB tests; opt-in live tests
docs/ DATA_SOURCES.md (terms + decisions), OPERATIONS.md, GENRE_VIBE.md,
RECOMMENDATIONS.md, FRONTEND.md, INSTAGRAM.md, sources/*.md
NormalizedListing
rows. The runner upserts them (upsert_listing, keyed by (source, source_id)), tracks first/last
seen, content changes and disappearances (tombstones), and stores every price tier.resolve_listing() links each listing to a canonical event: hard cross-references
first (a venue page linking a DICE id), then venue family + nightlife date + trigram title + lineup
overlap. Grey-zone matches land in match_candidate for review; canonical fields are recomputed from
the highest-priority live listing (refresh_event).event_tag)./api/feed reads event_feed (RLS-guarded view) and returns the weekend in the UI's
shape, cached at the edge for five minutes.npm install
cp .env.example .env # fill DATABASE_URL (local Postgres is fine) and optional keys
bash scripts/db-local.sh noct # creates the DB and applies every migration
npm run fetch -- ra --limit 5 # dry-run one adapter (no DB)
DATABASE_URL=postgresql://localhost:5432/noct npm run ingest -- ra elsewhere publicrecords goodroom
DATABASE_URL=postgresql://localhost:5432/noct npm run enrich -- --limit 20 # rules-only unless an LLM key is set (docs/GENRE_VIBE.md → Provider options)
npm test # unit + DB tests (DB tests skip without DATABASE_URL); NOCT_LIVE=1 adds live tests
npx vercel dev # serves index.html + /api/*
supabase link --project-ref <ref>, supabase db push. Copy the
Supavisor transaction pooler URL (IPv4, port 6543) into DATABASE_URL.vercel link, set env vars from .env.example (DATABASE_URL, CRON_SECRET, keys),
vercel deploy --prod. vercel.json registers the daily fallback crons (Hobby limit).vercel_base_url and cron_secret into app_setting so pg_cron can call
/api/ingest/* every few hours (docs/OPERATIONS.md)./api/ingest/ra?limit=5 once and read /api/health. If a source shows BLOCKED:, that host
rejects datacenter traffic — see docs/DATA_SOURCES.md; NOCT does not work around blocks.This is the beginning of the README. Read the rest at the original. Read the full README ↗