192 lines
7.2 KiB
TypeScript
192 lines
7.2 KiB
TypeScript
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<string, any>;
|
|
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");
|
|
|
|
if (payload?.events[0]?.message) {
|
|
const message = payload.events[0].message.text;
|
|
|
|
if (message === "เมนูหลัก > ข้อความ") {
|
|
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: {
|
|
employee: {
|
|
customerBranch: {
|
|
OR: [
|
|
{ userId: userIdLine },
|
|
{
|
|
customer: {
|
|
branch: { some: { userId: userIdLine } },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
expireDate: {
|
|
lt: dataNow.add(30, "day").toDate(),
|
|
},
|
|
},
|
|
orderBy: {
|
|
expireDate: "asc",
|
|
},
|
|
});
|
|
|
|
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");
|
|
|
|
if (diffDate > 0) {
|
|
return `${index + 1}. คุณ${item.firstName} ${item.lastName} วันหมดอายุเอกสาร : ${dateFormat} ใกล้หมดอายุอีก ${diffDate} วัน\n ${process.env.LINE_LIFF_URL}/${item.employeeId}`;
|
|
}
|
|
return `${index + 1}. คุณ${item.firstName} ${item.lastName} วันหมดอายุเอกสาร : ${dateFormat} หมดอายุไปแล้ว ${Math.abs(diffDate)} วัน \n ${process.env.LINE_LIFF_URL}/${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" };
|
|
}
|
|
}
|