import { Elysia } from 'elysia'; import * as line from '@line/bot-sdk'; import { lineConfig, lineClient } from '../lib/lineClient'; import crypto from 'crypto'; const webhookHandler = async ({ request, body, set }: { request: Request, body: any, set: any }) => { // 1. Verify Signature const signature = request.headers.get('x-line-signature'); if (!signature) { set.status = 400; return { error: 'Missing Signature' }; } // xxIn our global onParse hook, we intercepted the body and returned it as a raw text string. const rawBody = typeof body === 'string' ? body : JSON.stringify(body); if (!rawBody) { set.status = 400; return { error: 'Empty Body' }; } const hash = crypto .createHmac('SHA256', lineConfig.channelSecret) .update(rawBody) .digest('base64'); console.log('[LINE Webhook] Signature Check - Received:', signature, 'Calculated:', hash); if (hash !== signature) { console.error('[LINE Webhook] Invalid Signature Blocked'); set.status = 401; return { error: 'Invalid Signature' }; } try { const parsedBody = typeof body === 'string' ? JSON.parse(body) : body; const events: line.WebhookEvent[] = parsedBody.events || []; for (const event of events) { if (event.type === 'message' && event.message.type === 'text') { // Ignore verification dummy tokens if (event.replyToken === '00000000000000000000000000000000' || event.replyToken === 'ffffffffffffffffffffffffffffffff') { continue; } // Simple echo bot for now await lineClient.replyMessage({ replyToken: event.replyToken, messages: [{ type: 'text', text: `คุณส่งข้อความว่า: ${event.message.text}` }] }); } } return { success: true }; } catch (err) { console.error('[LINE Webhook] Error:', err); set.status = 500; return { error: 'Webhook processing failed' }; } }; export const lineRoutes = new Elysia({ prefix: '/line' }) .post('/webhook', webhookHandler) .post('/webhook/', webhookHandler);