mirror of
https://github.com/tiennm99/claude-status-webhook.git
synced 2026-04-17 13:21:01 +00:00
Cloudflare Workers bot that forwards status.claude.com (Atlassian Statuspage) incident and component updates to subscribed Telegram users via CF Queues fan-out. - Hono.js routing with grammY webhook handler - Bot commands: /start, /stop, /status, /subscribe - Supergroup topic support (message_thread_id) - KV store with claude-status: prefix and composite keys - Queue consumer with batch send, 403 auto-removal, 429 retry - Timing-safe webhook secret validation - HTML escaping for Telegram messages
54 lines
1.5 KiB
JavaScript
54 lines
1.5 KiB
JavaScript
const STATUS_API = "https://status.claude.com/api/v2/summary.json";
|
|
|
|
/**
|
|
* Fetch all components from status.claude.com (excludes group-level entries)
|
|
*/
|
|
export async function fetchAllComponents() {
|
|
const res = await fetch(STATUS_API);
|
|
if (!res.ok) throw new Error(`Status API returned ${res.status}`);
|
|
const data = await res.json();
|
|
return data.components.filter((c) => !c.group);
|
|
}
|
|
|
|
/**
|
|
* Fuzzy match a component by name (case-insensitive includes)
|
|
*/
|
|
export async function fetchComponentByName(name) {
|
|
const components = await fetchAllComponents();
|
|
return components.find((c) =>
|
|
c.name.toLowerCase().includes(name.toLowerCase())
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Human-readable status label
|
|
*/
|
|
export function humanizeStatus(status) {
|
|
const map = {
|
|
operational: "Operational",
|
|
degraded_performance: "Degraded Performance",
|
|
partial_outage: "Partial Outage",
|
|
major_outage: "Major Outage",
|
|
under_maintenance: "Under Maintenance",
|
|
investigating: "Investigating",
|
|
identified: "Identified",
|
|
monitoring: "Monitoring",
|
|
resolved: "Resolved",
|
|
};
|
|
return map[status] || status;
|
|
}
|
|
|
|
/**
|
|
* Escape HTML special chars for Telegram's HTML parse mode
|
|
*/
|
|
export function escapeHtml(s) {
|
|
return s?.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") ?? "";
|
|
}
|
|
|
|
/**
|
|
* Format a single component as HTML line
|
|
*/
|
|
export function formatComponentLine(component) {
|
|
return `<b>${escapeHtml(component.name)}</b>: <code>${humanizeStatus(component.status)}</code>`;
|
|
}
|