const express = require('express'); const { chromium } = require('playwright'); const fs = require('fs'); const path = require('path'); // ========================= // CONFIG // ========================= const DATA_FILE = 'latest.json'; const DOWNLOAD_DIR = 'downloads'; // Check every 5 minutes const CHECK_INTERVAL = 5 * 60 * 1000; const app = express(); const PORT = process.env.PORT || 7860; // ========================= // CREATE DOWNLOAD FOLDER // ========================= if (!fs.existsSync(DOWNLOAD_DIR)) { fs.mkdirSync(DOWNLOAD_DIR); } // ========================= // READ OLD DATA // ========================= let oldData = {}; if (fs.existsSync(DATA_FILE)) { oldData = JSON.parse( fs.readFileSync(DATA_FILE, 'utf-8') ); } // ========================= // URLS // ========================= const urls = [ { name: "Guidelines and Help Files", url: "https://admissions.jnvuiums.in/ViewAllNews.aspx?type=D&Heading=Guidelines%20and%20Help%20Files%20(Download%20Section)", selector: "a[href*='DownloadAttachment']" }, { name: "News and Events", url: "https://admissions.jnvuiums.in/ViewAllNews.aspx?type=N&&Heading=News%20and%20Events", selector: "ul.important_announcement li:nth-of-type(2)" }, { name: "Time Table", url: "https://erp.jnvuiums.in/CollegePortal/General_Inst_Notification.aspx?type=O&&Heading=Examination%20Time%20Table" }, { name: "General Instructions and Notification", url: "https://erp.jnvuiums.in/CollegePortal/General_Inst_Notification.aspx?type=G&&Heading=General%20Instructions%20and%20Notification" } ]; // ========================= // MAIN FUNCTION // ========================= async function startChecking() { const browser = await chromium.launch({ headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox' ] }); const context = await browser.newContext({ acceptDownloads: true }); const page = await context.newPage(); // ========================= // SCRAPE FUNCTION // ========================= async function scrapeItem(item) { console.log("\n=============================="); console.log("CHECKING:", item.name); console.log("==============================\n"); await page.goto(item.url, { waitUntil: 'networkidle' }); await page.waitForTimeout(3000); // ========================= // NEWS SECTION // ========================= if (item.name === "News and Events") { await page.waitForSelector(item.selector); const latestText = await page .locator(item.selector) .innerText(); console.log("LATEST NEWS:\n"); console.log(latestText); const oldTitle = oldData[item.name]?.title; if (oldTitle !== latestText) { console.log("\nšŸ”„ NEW NEWS UPDATE FOUND šŸ”„"); console.log("\nOLD :"); console.log(oldTitle || "No old data"); console.log("\nNEW :"); console.log(latestText); oldData[item.name] = { title: latestText }; } else { console.log("\nāœ… NO NEW NEWS UPDATE"); } return; } // ========================= // GUIDELINES SECTION // ========================= if (item.name === "Guidelines and Help Files") { const element = page .locator(item.selector) .first(); const title = await element.innerText(); console.log("TITLE :\n"); console.log(title); const oldTitle = oldData[item.name]?.title; if (oldTitle !== title) { console.log("\nšŸ”„ NEW UPDATE FOUND šŸ”„"); console.log("\nOLD :"); console.log(oldTitle || "No old data"); console.log("\nNEW :"); console.log(title); const [ download ] = await Promise.all([ page.waitForEvent('download'), element.click() ]); const fileName = download.suggestedFilename(); const savePath = path.join( DOWNLOAD_DIR, fileName ); await download.saveAs(savePath); console.log("\nāœ… PDF DOWNLOADED"); console.log("šŸ“ SAVED:"); console.log(savePath); oldData[item.name] = { title: title }; } else { console.log("\nāœ… NO NEW UPDATE"); } return; } // ========================= // TIME TABLE + NOTIFICATION // ========================= const row = page.locator( '#ctl00_ContentPlaceHolder1_GridData tr' ).nth(1); const title = await row .locator('td') .nth(1) .innerText(); const element = row.locator( 'a[id*="LnkBtnDownload"]' ); console.log("TITLE :\n"); console.log(title); const oldTitle = oldData[item.name]?.title; if (oldTitle !== title) { console.log("\nšŸ”„ NEW UPDATE FOUND šŸ”„"); console.log("\nOLD :"); console.log(oldTitle || "No old data"); console.log("\nNEW :"); console.log(title); const [ download ] = await Promise.all([ page.waitForEvent('download'), element.click() ]); const fileName = download.suggestedFilename(); const savePath = path.join( DOWNLOAD_DIR, fileName ); await download.saveAs(savePath); console.log("\nāœ… PDF DOWNLOADED"); console.log("šŸ“ SAVED:"); console.log(savePath); oldData[item.name] = { title: title }; } else { console.log("\nāœ… NO NEW UPDATE"); } } // ========================= // LOOP ALL URLS // ========================= for (const item of urls) { try { await scrapeItem(item); } catch (err) { console.log("\nāŒ ERROR IN:", item.name); console.log(err.message); } } // ========================= // SAVE JSON // ========================= fs.writeFileSync( DATA_FILE, JSON.stringify(oldData, null, 2) ); console.log("\n=============================="); console.log("āœ… ALL CHECK COMPLETE"); console.log("==============================\n"); await browser.close(); } // ========================= // FIRST RUN // ========================= startChecking(); // ========================= // AUTO CHECK EVERY 5 MIN // ========================= setInterval(async () => { console.log("\nā³ CHECKING AGAIN...\n"); await startChecking(); }, CHECK_INTERVAL); // ========================= // EXPRESS SERVER // ========================= app.get('/', (req, res) => { res.send('JNVU Monitor Running āœ…'); }); app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });