/** * Telegram Bot Web Chat UI - Cloudflare Worker * * Endpoints: * - GET / : Web chat UI * - POST /api/chat : JSON API * - GET /health : Health check */ interface Env { BOT_TOKEN: string; WEBHOOK_SECRET: string; CHAT_ID: string; BOT_WORKER_URL: string; BOT_WORKER?: Fetcher; // Service Binding } interface ChatRequest { message: string; } interface ChatResponse { response: string; time_ms: number; } /** * Main Worker fetch handler */ export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); // CORS headers for API const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }; // Handle CORS preflight if (request.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); } try { // Route handling if (url.pathname === '/' && request.method === 'GET') { return handleWebUI(env); } if (url.pathname === '/api/chat' && request.method === 'POST') { return await handleChatAPI(request, env, corsHeaders); } if (url.pathname === '/health' && request.method === 'GET') { return new Response(JSON.stringify({ status: 'ok', timestamp: new Date().toISOString() }), { headers: { 'Content-Type': 'application/json', ...corsHeaders }, }); } return new Response('Not Found', { status: 404 }); } catch (error) { console.error('[Worker] Error:', error); return new Response(JSON.stringify({ error: 'Internal Server Error', message: error instanceof Error ? error.message : String(error) }), { status: 500, headers: { 'Content-Type': 'application/json', ...corsHeaders }, }); } }, }; /** * Handle Web UI (GET /) */ function handleWebUI(env: Env): Response { const html = ` Telegram Bot Chat

πŸ€– Telegram Bot Chat

User ID: ${env.CHAT_ID}
`; return new Response(html, { headers: { 'Content-Type': 'text/html; charset=utf-8' }, }); } /** * Handle Chat API (POST /api/chat) */ async function handleChatAPI( request: Request, env: Env, corsHeaders: Record ): Promise { const startTime = Date.now(); try { // Parse request const body = await request.json() as ChatRequest; const message = body.message?.trim(); if (!message) { return new Response(JSON.stringify({ error: 'Message is required' }), { status: 400, headers: { 'Content-Type': 'application/json', ...corsHeaders }, }); } // Call bot worker /api/chat endpoint (Service Binding μš°μ„ , fallback: URL) const requestInit = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${env.WEBHOOK_SECRET}`, }, body: JSON.stringify({ message, chat_id: parseInt(env.CHAT_ID), user_id: parseInt(env.CHAT_ID), username: 'web-tester', }), }; const response = env.BOT_WORKER ? await env.BOT_WORKER.fetch('https://internal/api/chat', requestInit) : await fetch(`${env.BOT_WORKER_URL}/api/chat`, requestInit); if (!response.ok) { const errorText = await response.text(); throw new Error(`Bot Worker error: ${response.status} - ${errorText}`); } const data = await response.json() as any; if (data.error) { throw new Error(data.error); } const duration = Date.now() - startTime; const result: ChatResponse = { response: data.response || 'No response from bot', time_ms: duration, }; return new Response(JSON.stringify(result), { headers: { 'Content-Type': 'application/json', ...corsHeaders }, }); } catch (error) { console.error('[ChatAPI] Error:', error); return new Response(JSON.stringify({ error: 'Failed to process message', message: error instanceof Error ? error.message : String(error), }), { status: 500, headers: { 'Content-Type': 'application/json', ...corsHeaders }, }); } }