import { Body, Controller, Get, Post, Query, Response, Route, Tags } from "tsoa"; import prisma from "../db"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import timezone from "dayjs/plugin/timezone"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import { notFoundError } from "../utils/error"; dayjs.extend(utc); dayjs.extend(timezone); interface WebhookPayload { destination?: string; events: Array<{ mode?: string; deliveryContext?: Record; webhookEventId?: string; type: string; replyToken: string; source: { userId: string; type: string; }; timestamp: number; message: { id?: string; type: string; text?: string; quoteToken?: string; stickerId?: string; packageId?: string; stickerResourceType?: string; keywords?: string[]; emojis?: string[] | { productId: string; emojiId: string; index: number; length: number }[]; }; }>; } interface accessToken { code: string; state: string; } @Route("api/v1/webhook") @Tags("Webhook") export class WebHookController extends Controller { async #getLineToken() { if (!process.env.LINE_MESSAGING_API_TOKEN) { console.warn("Line Webhook Activated but LINE_MESSAGING_API_TOKEN not set."); throw new HttpError(HttpStatus.NOT_IMPLEMENTED, "NOT IMPLEMENTED", "notImplemented"); } return process.env.LINE_MESSAGING_API_TOKEN; } @Post() @Response(200, "Webhook received successfully") public async receiveWebhook(@Body() payload: WebhookPayload) { const token = await this.#getLineToken(); if (!payload || !payload.events || !Array.isArray(payload.events)) { this.setStatus(400); return { message: "Invalid payload structure" }; } if (payload.events.length > 0) { const userIdLine = payload.events[0]?.source?.userId; const dataNow = dayjs().tz("Asia/Bangkok").startOf("day"); // const dataUser = await prisma.customerBranch.findFirst({ // where:{ // userId:userIdLine // } // }) const dataEmployee = await prisma.employeePassport.findMany({ select: { firstName: true, firstNameEN: true, lastName: true, lastNameEN: true, employeeId: true, expireDate: true, employee: { select: { firstName: true, lastName: true, customerBranch: { select: { firstName: true, firstNameEN: true, lastName: true, lastNameEN: true, customerName: true, customer: { select: { customerType: true, registeredBranch: { select: { telephoneNo: true, }, }, }, }, }, }, }, }, }, where: { expireDate: { lt: dataNow.add(30, "day").toDate(), }, }, orderBy: { expireDate: "asc", }, }); if (payload?.events[0]?.message) { const message = payload.events[0].message.text; if (message === "เมนูหลัก > ข้อความ") { const dataUser = userIdLine; const textHead = "JWS ALERT:"; let textData = ""; if (dataEmployee.length > 0) { const customerName = dataEmployee[0]?.employee?.customerBranch?.customerName ?? "ไม่ระบุ"; const telephoneNo = dataEmployee[0]?.employee?.customerBranch?.customer.registeredBranch.telephoneNo ?? "ไม่ระบุ"; const textEmployer = `เรียน คุณ${customerName}`; const textAlert = "ขอแจ้งให้ทราบว่าหนังสือเดินทางของลูกจ้าง"; const textAlert2 = "และจำเป็นต้องดำเนินการต่ออายุในเร็ว ๆ นี้"; const textExpDate = "🔹 กรุณาตรวจสอบและดำเนินการต่ออายุภายในวันที่กำหนด เพื่อป้องกันปัญหาด้านเอกสารหรือการเดินทางที่อาจเกิดขึ้น"; const textAlert3 = "หากดำเนินการเรียบร้อยแล้ว กรุณาแจ้งให้ฝ่ายที่เกี่ยวข้องทราบ 🙏"; let textFooter = `📞 สอบถามข้อมูลเพิ่มเติม: ${telephoneNo}`; const textEmployees = dataEmployee .map((item, index) => { const dateFormat = dayjs(item.expireDate).format("DD/MM/") + (dayjs(item.expireDate).year() + 543); const diffDate = dayjs(item.expireDate).diff(dayjs(), "day"); return `${index + 1}. คุณ${item.firstName} ${item.lastName} วันหมดอายุเอกสาร : ${dateFormat} ใกล้หมดอายุอีก ${diffDate} วัน\n https://taii-cmm.case-collection.com/api/v1/line/employee/${item.employeeId}`; }) .join("\n"); textData = `${textHead}\n\n${textEmployer}\n\n${textAlert}\n${textEmployees}\n${textAlert2}\n\n${textExpDate}\n\n${textAlert3}\n\n${textFooter}`; } else { textData = `${textHead}\n\nขออภัย ไม่พบข้อมูลหนังสือเดินทางที่มีกำหนดหมดอายุภายใน 30 วันข้างหน้า 🙏`; } const data = { to: dataUser, messages: [ { type: "text", text: textData, }, ], }; await fetch("https://api.line.me/v2/bot/message/push", { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify(data), }); } } } return { message: "Webhook received successfully" }; } }