123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- import 'dotenv/config'
- import cron from "node-cron";
- import dayjs from "dayjs";
- import axios from "axios";
- import prisma from "../prisma/PrismaClient";
- import { formatISOWithoutTimezone } from './FormatDate';
- const queue: (() => Promise<void>)[] = [];
- let processing = false;
- const API_URL_NOTIFICATION = process.env.API_TOKEN_NOTIFICATION;
- const API_TOKEN_NOTIFICATTION = process.env.API_TOKEN_NOTIFICATTION;
- const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
- const randomDelay = () => {
- const min = 60 * 1000; // 60 detik
- const max = 180 * 1000; // 180 detik
- return Math.floor(Math.random() * (max - min + 1)) + min;
- };
- // Worker untuk memproses queue
- const processQueue = async () => {
- if (processing || queue.length === 0) return;
- processing = true;
- while (queue.length > 0) {
- console.log(`▶️ Mulai memproses antrean, sisa job: ${queue.length}`);
- const job = queue.shift();
- if (job) {
- try {
- await job();
- if (queue.length > 0) {
- const delayMs = randomDelay();
- console.log(`⏳ Tunggu ${Math.round(delayMs / 1000)} detik sebelum job berikutnya...`);
- await delay(delayMs);
- }
- } catch (err: any) {
- console.error("❌ Job gagal:", err.message);
- }
- }
- }
- console.log("✅ Semua job selesai diproses.");
- processing = false;
- };
- // Tambah job ke antrean
- const addToQueue = (fn: () => Promise<void>) => {
- queue.push(fn);
- console.log(`➕ Job ditambahkan ke antrean. Total sekarang: ${queue.length}`);
- console.log("📦 Isi antrean saat ini:", queue.map((_, i) => `Job#${i + 1}`));
- processQueue();
- };
- // Cron: jalan tiap hari
- cron.schedule("00 08 * * *", async () => {
- console.log("⏰ [CRON] Checking schedules for today...");
- try {
- const today = dayjs().startOf("day").toDate();
- const tomorrow = dayjs().add(1, "day").startOf("day").toDate();
- // Ambil semua schedule untuk hari ini
- const schedules = await prisma.scheduleVisitation.findMany({
- where: {
- deletedAt: null,
- date_visit: {
- gte: today,
- lt: tomorrow,
- },
- },
- include: {
- sales_visit: true,
- hospital: {
- include: {
- province: true,
- city: true,
- }
- }
- },
- });
- if (schedules.length === 0) {
- console.log("ℹ️ No schedules for today.");
- return;
- }
- console.log(`📌 Ditemukan ${schedules.length} schedule untuk hari ini:`);
- schedules.forEach((s, idx) => {
- console.log(` #${idx + 1} SalesID=${s.sales_id}, Phone=${s.sales_visit?.phone ?? "❌ kosong"}`);
- });
- schedules.forEach((schedule, idx) => {
- const phone = schedule.sales_visit?.phone as string | undefined;
- if (!phone) {
- console.warn(`⚠️ Sales ${schedule.sales_id} tidak punya nomor telepon.`);
- return;
- }
- const fullname = schedule.sales_visit?.fullname || '-';
- const hospitalName = schedule.hospital?.name || '-';
- const hospitalAddress = schedule.hospital?.address || '-';
- const hospitalContact = schedule.hospital?.contact || '-';
- const hospitalEmail = schedule.hospital?.email || '-';
- const hospitalLat = schedule.hospital?.latitude || '-';
- const hospitalLong = schedule.hospital?.longitude || '-';
- const hospitalGmaps = schedule.hospital?.gmaps_url || '-';
- const hospitalProv = schedule.hospital?.province.name || '-';
- const hospitalCity = schedule.hospital?.city.name || '-';
- const scheduleCreate = schedule.createdAt || '-';
- const message = `
- Halo ${fullname},
- [[[ maaf, ini sedang testing - gayuh ]]]
- Ini adalah pengingat jadwal kunjungan yang dibuat pada tanggal ${formatISOWithoutTimezone(scheduleCreate)}.
- Anda dijadwalkan untuk mengunjungi:
- Rumah Sakit: ${hospitalName}
- Alamat: ${hospitalAddress}
- Kota: ${hospitalCity}, Provinsi: ${hospitalProv}
- Kontak: ${hospitalContact}, Email: ${hospitalEmail}
- Lokasi Google Maps: ${hospitalGmaps}
- Koordinat: Latitude ${hospitalLat}, Longitude ${hospitalLong}
- Nomor telepon sales terkait: ${phone}
- Pastikan semua persiapan kunjungan sudah lengkap! Semangat bertugas!
- `.trim();
- addToQueue(async () => {
- console.log(
- `[${dayjs().format("HH:mm:ss")}] 📩 (Job#${idx + 1}) Sending message to ${phone}...`
- );
- await axios.post(
- API_URL_NOTIFICATION!,
- { to: phone, message },
- {
- headers: {
- "x-api-token": API_TOKEN_NOTIFICATTION!,
- "Content-Type": "application/json",
- },
- }
- );
- console.log(
- `[${dayjs().format("HH:mm:ss")}] ✔️ (Job#${idx + 1}) Sent message to ${phone}`
- );
- });
- });
- } catch (err: any) {
- console.error("❌ Cron job error:", err.message);
- }
- });
|