UNPKG

naukri-automation-tool

Version:

Naukri automation tool to fetch, filter, and apply for jobs automatically using gen ai.

310 lines (290 loc) 9.7 kB
const fs = require("fs"); const path = require("path"); const readline = require("readline"); const { checkSuitability } = require("./gemini"); const { localStorage } = require("./helper"); const prompts = require("@inquirer/prompts"); const { get } = require("http"); const createCsvWriter = require("csv-writer").createObjectCsvWriter; const { getDataFromFile, getFileData, writeToFile, writeFileData } = require("./ioUtils"); const { matchScoreAPI } = require("./api"); let selectedProfile = null; // Split an array into chunks of a given size const chunkArray = (array, size) => { return Array.from({ length: Math.ceil(array.length / size) }, (_, index) => array.slice(index * size, index * size + size) ); }; // Create an interface to read from the console const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); // Ask the question and return the answer const askQuestion = async (question) => { return new Promise((resolve, reject) => { rl.question(question, (answer) => { resolve(answer); }); }); }; const filterJobs = (jobInfo) => { const user = localStorage.getItem("profile"); const preferredSalary = user.profile.expectedCtc ?? 0; const maxTime = 30; const maxApplyCount = 10000; const experience = user.profile.totalExperience.year + 2 ?? 100; const videoProfile = false; const vacany = 1; const filteredJobs = jobInfo .filter((jobDetails) => { const createdDays = jobDetails.createdDate ? (new Date() - new Date(jobDetails.createdDate)) / (1000 * 60 * 60 * 24) : 0; return ( jobDetails.maximumSalary >= preferredSalary && createdDays <= maxTime && jobDetails.applyCount <= maxApplyCount && jobDetails.minimumExperience <= experience && (jobDetails.videoProfilePreferred === undefined || jobDetails.videoProfilePreferred === videoProfile) && jobDetails.applyRedirectUrl === undefined && (jobDetails.vacany === undefined || jobDetails.vacany >= vacany) ); }) .map((jobDetails) => ({ jobId: jobDetails.jobId, jobTitle: jobDetails.title, companyName: jobDetails.companyName, description: jobDetails.description, minimumSalary: jobDetails.minimumSalary, maximumSalary: jobDetails.maximumSalary, matchScore: jobDetails.matchScore, })) .sort((a, b) => b.maximumSalary - a.maximumSalary); console.log( `Total number of Jobs : ${jobInfo.length}\nFiltered Jobs : ${filteredJobs.length}` ); return filteredJobs; }; const getEmailsIds = async (jobs, profile) => { let emailIds = await getDataFromFile("hrEmails"); if (!emailIds) emailIds = []; jobs?.forEach((jobDetails) => { if(jobDetails.matchScore == 0) return; const emailId = jobDetails.description.match( /([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi ); const contact = jobDetails.description.match( /\b(?:\+?(\d{1,3})[-.\s]?)?(\(?\d{3}\)?|\d{3})[-.\s]?\d{3}[-.\s]?\d{4}\b/g ); if (emailId && emailId.length > 0) { const index = emailIds.filter( (emailObj) => emailObj.email[0] === emailId[0] ); if (index.length === 0) emailIds.push({ company: jobDetails.companyName, email: emailId, title: jobDetails.title, jobId: jobDetails.jobId, contact: contact, }); } }); writeToFile(emailIds, "hrEmails"); getCsvFile(emailIds, `${profile}-hrContactDetails.csv`); return emailIds; }; // select a profile const selectProfile = async () => { let selectedProfile = null; // if (profiles.length === 1) { // selectedProfile = profiles[0]; // } const profiles = await getFileData("profiles"); if (!profiles || profiles.length === 0) { console.log("No profiles found. Please add profiles to continue."); return null; } while (!selectedProfile) { const profileOptions = profiles?.map((profile, index) => ({ name: profile.id, value: index + 1, })); profileOptions.push({ name: "Create New Profile", value: -1 }); const ans = await prompts.select({ message: `Select a profile from the following list`, choices: profileOptions, }); if (ans === -1) return null; const index = parseInt(ans, 10); // Convert ans to a number if (isNaN(index) || index <= 0 || index > profiles.length) { console.log("Invalid profile number. Please try again."); } else { selectedProfile = profiles[index - 1]; } } return selectedProfile; }; const matchKeywords = async (keywords, jobKeywords) => { if (!keywords || keywords.length === 0) { preferences = await getDataFromFile("preferences"); if (!preferences.keywords || preferences.keywords.length === 0) { const res = await prompts.input({ message: "Please enter keywords to match with job description\n", }); keywords = res.split(","); const preferences = getDataFromFile("preferences"); preferences.keywords = keywords; writeToFile(preferences, "preferences"); } else { keywords = preferences.keywords; } } const isMatched = keywords.some((keyword) => jobKeywords?.toLowerCase().includes(keyword.toLowerCase()) ); return isMatched; }; const aiMatching = async (jobInfo, profile) => { try { const res = await checkSuitability(jobInfo, profile); return res.isSuitable; } catch (e) { console.log("Error in AI matching, Switching to manual matching"); return await manualMatching(jobInfo); } }; const manualMatching = async (jobInfo) => { const res = await prompts.select({ message: ` Is this job suitable: ${jobInfo.jobTitle} ?`, choices: [ { name: "Yes", value: true }, { name: "No", value: false }, ], }); return res; }; const naukriMatching = async (jobInfo, profile) => { // Check if matchScore exists and is explicitly 0 or null/undefined if (jobInfo.matchScore == 0 ) { return false; } else if (jobInfo.matchScore == null || jobInfo.matchScore == undefined) { const matchScore = await matchScoreAPI(jobInfo.jobId); if(matchScore.Keyskills == 0){ return false; } } return true; }; const matchingMethods = { ai: aiMatching, manual: manualMatching, keywords: matchKeywords, naukriMatching: naukriMatching, }; const matchingStrategy = async (jobInfo, profile, matchDescription = false) => { const preferences = await getDataFromFile("preferences"); let matchingResult = false; let matchStrategy = "keywords"; if (preferences && preferences.matchStrategy) matchStrategy = preferences.matchStrategy; switch (matchStrategy) { case "naukriMatching": matchingResult = await matchingMethods.naukriMatching(jobInfo, profile); break; case "ai": matchingResult = await matchingMethods.ai(jobInfo, profile); break; case "manual": matchingResult = await matchingMethods.manual(jobInfo); break; default: matchingResult = await matchingMethods.keywords( profile.keywords, matchDescription ? jobInfo.description : jobInfo.jobTitle ); } return matchingResult; }; const getAnswerFromUser = async (question) => { let res; switch (question.questionType) { case "List Menu": case "Radio Button": res = await prompts.select({ message: question.questionName, choices: Object.values(question.answerOption).map((option) => ({ name: option, value: option, })), default: question.answer, }); question.answer = [res]; break; case "Check Box": res = await prompts.checkbox({ message: question.questionName, choices: Object.values(question.answerOption).map((option) => ({ name: option, value: option, })), default: question.answer, }); question.answer = res; break; default: res = await prompts.input({ message: question.questionName, default: question.answer, }); question.answer = res; } return question.answer; }; const compressProfile = (profile) => { delete profile.profile.keySkills; profile.employmentHistory.map((item) => delete item.jobDescription); return profile; }; const getCsvFile = async (data, fileName) => { try { const downloadsFolder = path.join(require("os").homedir(), "Downloads"); //Check if the folder exists if (!fs.existsSync(downloadsFolder)) { fs.mkdirSync(downloadsFolder, { recursive: true }); } const csvWriter = createCsvWriter({ path: path.join(downloadsFolder, fileName), // Save file to Downloads folder header: Object.keys(data[0]).map((key) => ({ id: key, title: key })), }); csvWriter .writeRecords(data) .then(() => console.log("CSV file was written successfully to the Downloads folder!") ) .catch((err) => console.error("Error writing CSV file:", err.message)); } catch (e) { console.error("Error writing CSV file:", e.message); } }; module.exports = { chunkArray, askQuestion, writeToFile, writeFileData, filterJobs, getEmailsIds, rl, getDataFromFile, getFileData, selectProfile, selectedProfile, matchingMethods, matchingStrategy, getAnswerFromUser, compressProfile, getCsvFile, };