Skip to content

Commit 0dea3a0

Browse files
Map Zoho field names from real API responses
Update sponsor + ticket sanitizers to use the snake_case field names Zoho actually returns (company_name, sponsorship_type_name, website_url; amount, currency_code, sales_start_date, sales_end_date, quantity, sold, status_string). Drop sponsor logoUrl since Zoho's sponsor schema doesn't include it. Add quantity/sold/status to the ticket snapshot, and derive isSoldOut/isAvailable. Update SKILL.md table + workflows to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5b4f58d commit 0dea3a0

2 files changed

Lines changed: 44 additions & 21 deletions

File tree

‎scripts/fetch-zoho.mjs‎

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,30 +88,51 @@ async function zohoGet(path, token) {
8888
}
8989

9090
// Allowlist-based sanitization — anything not listed here is dropped, so a
91-
// schema change at Zoho cannot accidentally leak new fields into the
92-
// public repo.
91+
// schema change at Zoho cannot accidentally leak new fields (e.g. PII from
92+
// `contact` or `created_by`) into the public repo.
9393
function sanitizeSponsor(raw) {
9494
return {
9595
id: raw.id ?? null,
96-
name: raw.name ?? raw.companyName ?? null,
97-
tier: raw.tier ?? raw.level ?? raw.sponsorshipLevel ?? null,
98-
description: raw.description ?? raw.about ?? null,
99-
websiteUrl: raw.websiteUrl ?? raw.website ?? raw.url ?? null,
100-
logoUrl: raw.logoUrl ?? raw.logo ?? raw.image ?? null,
96+
name: raw.company_name ?? raw.name ?? null,
97+
tier: raw.sponsorship_type_name ?? raw.tier ?? null,
98+
description: raw.description ?? null,
99+
websiteUrl: raw.website_url ?? raw.websiteUrl ?? null,
101100
};
102101
}
103102

104103
function sanitizeTicket(raw) {
104+
const quantity = raw.quantity ?? null;
105+
const sold = raw.sold ?? null;
106+
const statusString = raw.status_string ?? null;
107+
const hidden = raw.hidden ?? null;
108+
109+
const isSoldOut =
110+
statusString === "sold_out"
111+
? true
112+
: typeof quantity === "number" && typeof sold === "number"
113+
? sold >= quantity
114+
: null;
115+
116+
const isAvailable =
117+
statusString === "active" && hidden === false
118+
? true
119+
: statusString != null || hidden != null
120+
? false
121+
: null;
122+
105123
return {
106124
id: raw.id ?? null,
107-
name: raw.name ?? raw.ticketName ?? null,
125+
name: raw.name ?? null,
108126
description: raw.description ?? null,
109-
price: raw.price ?? raw.amount ?? null,
110-
currency: raw.currency ?? raw.currencyCode ?? null,
111-
saleStartsAt: raw.saleStartsAt ?? raw.salesStart ?? null,
112-
saleEndsAt: raw.saleEndsAt ?? raw.salesEnd ?? null,
113-
isSoldOut: raw.isSoldOut ?? raw.soldOut ?? null,
114-
isAvailable: raw.isAvailable ?? null,
127+
price: raw.amount ?? raw.price ?? null,
128+
currency: raw.currency_code ?? raw.currency ?? null,
129+
quantity,
130+
sold,
131+
saleStartsAt: raw.sales_start_date ?? null,
132+
saleEndsAt: raw.sales_end_date ?? null,
133+
status: statusString,
134+
isSoldOut,
135+
isAvailable,
115136
};
116137
}
117138

‎skills/techbash/SKILL.md‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ Sponsor and ticket data come from committed JSON snapshots that organizers refre
121121

122122
| File | Content |
123123
| --- | --- |
124-
| `skills/techbash/data/sponsors.json` | `{ event, source, fetchedAt, sponsors[] }` — each sponsor has `id, name, tier, description, websiteUrl, logoUrl`. |
125-
| `skills/techbash/data/tickets.json` | `{ event, source, fetchedAt, tickets[] }` — each ticket has `id, name, description, price, currency, saleStartsAt, saleEndsAt, isSoldOut, isAvailable`. |
124+
| `skills/techbash/data/sponsors.json` | `{ event, source, fetchedAt, sponsors[] }` — each sponsor has `id, name, tier, description, websiteUrl`. The description may contain HTML; strip tags before display. |
125+
| `skills/techbash/data/tickets.json` | `{ event, source, fetchedAt, tickets[] }` — each ticket has `id, name, description, price, currency, quantity, sold, saleStartsAt, saleEndsAt, status, isSoldOut, isAvailable`. `price` is a number in the units of `currency` (e.g. `999` + `USD` means $999). |
126126

127127
When answering sponsor/ticket questions:
128128

@@ -226,16 +226,18 @@ Stack: Node 22, TypeScript, Azure Functions, GitHub Actions
226226

227227
1. Read `skills/techbash/data/sponsors.json`.
228228
2. If empty or `fetchedAt` is null, say "Sponsor list is not in the snapshot yet — see <https://techbash.com> for the current sponsor list." and stop.
229-
3. Group sponsors by `tier` (preserve any order present in the file; otherwise alphabetize by tier then by name).
230-
4. For each sponsor return name + one-line description (truncate longer descriptions) and the `websiteUrl` if present. Mention the `fetchedAt` timestamp at the bottom.
229+
3. Group sponsors by `tier` (preserve any order present in the file; otherwise sort tiers in conventional order — Platinum, Gold, Silver, Bronze, Community — falling back to alphabetical for unknown tiers; alphabetize sponsors within a tier by `name`).
230+
4. For each sponsor return `name` + one-line description (strip HTML tags from `description` and truncate longer descriptions to ~200 chars) and the `websiteUrl` if present.
231+
5. The snapshot does not include sponsor logos — for logos, direct the user to <https://techbash.com>. Mention the `fetchedAt` timestamp at the bottom.
231232

232233
### "What ticket types are available?" / "How much is a TechBash ticket?"
233234

234235
1. Read `skills/techbash/data/tickets.json`.
235236
2. If empty or `fetchedAt` is null, say "Ticket data is not in the snapshot yet — see <https://techbash.zohobackstage.com/TechBash2026>." and stop.
236-
3. List ticket types with name, description (truncated), and price formatted as `<price> <currency>`.
237-
4. Flag any ticket where `isSoldOut === true` as **SOLD OUT** at the top of its entry.
238-
5. Mention the `fetchedAt` timestamp and always close with "To buy, transfer, or refund tickets, go to <https://techbash.zohobackstage.com/TechBash2026>."
237+
3. List ticket types with `name`, description (strip HTML, truncate), and price formatted as `<price> <currency>` (e.g. `$999 USD`). If `currency` is null, just print the number.
238+
4. Flag any ticket where `isSoldOut === true` as **SOLD OUT** at the top of its entry. If `isSoldOut` is false and both `quantity` and `sold` are present, you may add a parenthetical "(N of M left)" where helpful.
239+
5. If `status` is something other than `active` (e.g. `inactive`, `hidden`), de-emphasize or omit those tickets from the main list — they are not currently for sale.
240+
6. Mention the `fetchedAt` timestamp and always close with "To buy, transfer, or refund tickets, go to <https://techbash.zohobackstage.com/TechBash2026>."
239241

240242
## Guardrails
241243

0 commit comments

Comments
 (0)