Public
Remixed
Event-driven CRM triage: Sheets + GPT-4o + Telegram C&C cards
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

Omni-Revenue Orchestrator (v3.0-CORE)

Event-driven CRM triage system. Hono middleware + React admin view + sequential pipeline (Ingest → Enrich → Draft → Review → Dispatch). Persistence in Google Sheets. Command & control via Telegram inline cards.

Status: code is deployed; you must provision env vars below before any pipeline call will succeed. The / dashboard renders without secrets but shows an error banner until GOOGLE_JSON is set.

Architecture

Rendering mermaid diagram...

Lifecycle / state machine

status_idlabelwritten by
0INGESTEDPOST /api/lead
1ENRICHED(collapsed into DRAFTED)
2DRAFTEDpipeline.enrichAndDraft
3REVIEWEDTelegram ✅ EXECUTE button
4DISPATCHEDpipeline.dispatch
5ERRORany failure path

Sheet layout (cols A..H)

timestamp | source | status_id | uid_hash | contact_raw | context_metadata | ai_payload | priority_score

uid_hash = SHA-256("${contact_raw}|${source}") — used as the idempotency key.

File map

index.ts                 ← HTTP routes + global error-boundary
config.ts                ← SHEET_ID, niche rules, admin chat id, model
types.ts                 ← STATUS enum, COL enum, Lead/AIPayload/Callback types
services/
  auth.ts                ← Google service-account JWT (RS256, in-mem cache)
  sheets.ts              ← typed Sheets v4 wrapper (append/read/update)
  idempotency.ts         ← SHA-256 uid_hash
  llm.ts                 ← GPT-4o "Elite Conversion" + niche map
  telegram.ts            ← bot client, C&C cards, callback parser
  pipeline.ts            ← lifecycle orchestrator (ingest/draft/dispatch)
cron/
  sweep.ts               ← 15-min reclamation task on A2:H35
frontend/
  index.html             ← shell (Twind)
  index.tsx              ← React entrypoint
  components/
    App.tsx              ← layout + status strip + auto-refresh
    LeadsTable.tsx       ← last 50 rows table w/ expandable drafts

Required environment variables

KeyWhat
GOOGLE_JSONFull service-account JSON (paste the entire file content). Must have edit on the sheet.
OPENAI_API_KEYOpenAI key with access to gpt-4o.
TELEGRAM_TOKENBot token from @BotFather. Used for both webhook auth (secret = part after :) and API.

Google Sheets prerequisite

  1. Share sheet 1YRlfO_L1bsyaPZPvenQfnpkZVcjUEYdIiQXo5BjfHcw with the service-account email (Editor).
  2. Sheet must have a header row 1 with the eight columns above (anything works as labels — the code only reads A2:H…).

Bring-up sequence

  1. Set the three env vars above.

  2. Hit GET /admin/init once — performs:

    • Service-account handshake (verifies sheet access, returns sample row count)
    • setWebhook against /api/telegram with the derived secret token
  3. POST a test lead:

    curl -X POST https://<your-val-url>/api/lead \ -H 'Content-Type: application/json' \ -d '{ "source": "manual", "contact_raw": "ada@example.com", "context_metadata": "Wants help with SEO for SaaS landing pages", "priority_score": 80 }'

    You should see:

    • row 2 (or next) appended to the sheet with status_id=0
    • Telegram alert 📥 INGESTED … to admin
  4. Wait ≤15 min for the sweep — or manually run cron/sweep.ts from the editor's "Run" button. The row will advance to status_id=2 (DRAFTED) and the admin gets a card with [✅ EXECUTE] [🔄 RE-GENERATE] [📊 VIEW RAW].

Niche-mapping logic

Defined in config.ts → NICHE_RULES. First-match-wins regex over context_metadata:

patternnicheinjected CTA
/seo/iseohttps://your-expert-link.com/seo-audit
/growth|scaling/igrowthhttps://your-expert-link.com/vsl
(fallback)generalhttps://your-expert-link.com/discovery

The LLM is instructed to use {{CTA_LINK}} as a placeholder; we substitute post-generation so the niche map is authoritative.

Error boundary

Every Hono route is wrapped by app.onError. Errors thrown from services/sheets.ts carry provider="google" + status; services/llm.ts carry provider="openai". The boundary fires tg.alertAdmin(...) for the two spec-mandated codes:

  • 429 + openai → "OpenAI 429 (rate-limit) — …"
  • 403 + google → "Google 403 (auth/quota) — …"

…plus a generic alert for any 5xx.

Telegram C&C protocol

Callback data format: ${ACTION}:${rowIndex} where ACTION ∈ {EXECUTE, REGEN, VIEW}. Only chat_id === 466679766 is honored; any other sender gets an "unauthorized" toast.

Local dev / customization

  • Retarget the sheet → edit SHEET_ID in config.ts.
  • Add a niche → push to NICHE_RULES.
  • Wire a real outbound channel → fill in pipeline.dispatch().
  • Sweep window → edit SWEEP_RANGE in config.ts and the interval delay on cron/sweep.ts (free tier minimum 15 min).

View source

View source on Val Town