Files
job-board-notifications/job-notifier/content.js

300 lines
8.5 KiB
JavaScript

// content.js - Handles job extraction and login automation for app.andela.com
console.log('[Andela Content Script] Loaded on', window.location.href);
function isLoginPage() {
var emailInput = document.querySelector('input[type="email"]#email');
var emailLabel = document.querySelector('label[for="email"]');
var hasLoginForm = !!(emailInput && emailLabel);
var isLoginUrl = window.location.pathname.indexOf('/login') === 0;
console.log(
'[Andela Content Script] Login check - hasForm:',
hasLoginForm,
'isLoginUrl:',
isLoginUrl,
);
return hasLoginForm || (isLoginUrl && !!emailInput);
}
function enterEmailAndClickLogin(email) {
try {
console.log('[Andela Content Script] Attempting to enter email:', email);
var emailInput = document.querySelector('input[type="email"]#email');
if (!emailInput) {
console.log('[Andela Content Script] Email input not found');
return false;
}
emailInput.focus();
emailInput.value = email;
emailInput.dispatchEvent(new Event('input', { bubbles: true }));
emailInput.dispatchEvent(new Event('change', { bubbles: true }));
emailInput.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true }));
console.log(
'[Andela Content Script] Email entered, looking for submit button...',
);
setTimeout(function () {
var submitButton = document.querySelector('button[type="submit"]');
if (!submitButton) {
var buttons = document.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
var btnText = buttons[i].textContent.toLowerCase();
if (
btnText.indexOf('login') !== -1 ||
btnText.indexOf('sign in') !== -1 ||
btnText.indexOf('continue') !== -1 ||
btnText.indexOf('submit') !== -1
) {
submitButton = buttons[i];
break;
}
}
}
if (submitButton) {
console.log(
'[Andela Content Script] Clicking submit button:',
submitButton.textContent,
);
submitButton.click();
} else {
console.log(
'[Andela Content Script] Submit button not found, trying form submit',
);
var form = emailInput.closest('form');
if (form) {
if (form.requestSubmit) form.requestSubmit();
else form.submit();
}
}
}, 500);
return true;
} catch (err) {
console.error('[Andela Content Script] Error entering email:', err);
return false;
}
}
// A section counts as "opportunities" if its heading contains this word —
// matches "Open Full-Stack Engineer opportunities", "Open AI Engineer
// opportunities", etc. Anything else (e.g. "Your applications") is
// treated as already-applied.
function isOpportunitySection(headingText) {
return /opportunit/i.test(headingText || '');
}
// Walks the page in DOM order and tags every "See more" button and every
// job-posting link with the heading text of the section it falls under.
function getPageSections() {
var elements = document.querySelectorAll(
'h3, button, a[href*="/job-postings/"]',
);
var currentHeading = '';
var seeMoreButtons = []; // { button, heading }
var jobLinks = []; // { link, heading }
elements.forEach(function (el) {
if (el.tagName === 'H3') {
currentHeading = el.textContent.trim();
console.log(
'[Andela Content Script] Section heading found:',
currentHeading,
);
return;
}
if (el.tagName === 'BUTTON') {
var text = (el.textContent || '').trim().toLowerCase();
if (text.indexOf('see') === 0 && text.indexOf('more') !== -1) {
seeMoreButtons.push({ button: el, heading: currentHeading });
}
return;
}
if (el.tagName === 'A') {
jobLinks.push({ link: el, heading: currentHeading });
}
});
return { seeMoreButtons: seeMoreButtons, jobLinks: jobLinks };
}
// Only expands "See more" under opportunity sections — leaves the
// applied-jobs section collapsed as-is.
function expandAllSeeMoreButtons(maxRounds) {
maxRounds = maxRounds || 25;
return new Promise(function (resolve) {
var rounds = 0;
function findSeeMoreButton() {
var sections = getPageSections();
for (var i = 0; i < sections.seeMoreButtons.length; i++) {
var entry = sections.seeMoreButtons[i];
if (isOpportunitySection(entry.heading)) {
return entry.button;
}
}
return null;
}
function clickNext() {
rounds++;
var btn = findSeeMoreButton();
if (!btn || rounds > maxRounds) {
console.log(
'[Andela Content Script] Done expanding opportunity "See more" buttons after',
rounds - 1,
'click(s)',
);
resolve();
return;
}
console.log('[Andela Content Script] Clicking:', btn.textContent.trim());
btn.click();
setTimeout(clickNext, 700);
}
clickNext();
});
}
function extractJobFromLink(link) {
var href = link.getAttribute('href') || '';
var idMatch = href.match(/\/job-postings\/(\d+)/);
var id = idMatch ? idMatch[1] : href;
var card =
link.closest('div.rounded-lg.border.border-neutral-200.bg-white') ||
link.parentElement;
var title = '';
var company = '';
var titleEl = card
? card.querySelector('.text-primary-900.font-semibold')
: null;
if (titleEl) {
var clone = titleEl.cloneNode(true);
var companySpan = clone.querySelector('span.font-normal');
if (companySpan) {
company = companySpan.textContent.replace(/^[\s—-]+/, '').trim();
companySpan.remove();
}
title = clone.textContent.trim();
}
var paragraphs = card ? card.querySelectorAll('p') : [];
var startInfo = paragraphs[0] ? paragraphs[0].textContent.trim() : '';
var locationInfo = paragraphs[1] ? paragraphs[1].textContent.trim() : '';
var skillEls = card
? card.querySelectorAll('.flex.flex-wrap.gap-2 span')
: [];
var skills = Array.prototype.map.call(skillEls, function (s) {
return s.textContent.trim();
});
var url = href;
if (url && url.indexOf('http') !== 0) {
url = window.location.origin + url;
}
return {
id: id,
title: title,
company: company || 'Company',
posted_date: startInfo || '',
location: locationInfo || '',
skills: skills,
url: url,
};
}
function extractJobsFromPage() {
console.log('[Andela Content Script] Extracting jobs...');
var jobs = [];
var seen = {};
var jobLinks = getPageSections().jobLinks;
console.log(
'[Andela Content Script] Found ' + jobLinks.length + ' job-posting links',
);
jobLinks.forEach(function (entry) {
try {
var job = extractJobFromLink(entry.link);
if (!job.id || seen[job.id]) return;
seen[job.id] = true;
job.section = entry.heading;
job.kind = isOpportunitySection(entry.heading)
? 'opportunity'
: 'applied';
if (job.title) {
jobs.push(job);
console.log(
'[Andela Content Script] Extracted job:',
job.title,
'(' + job.id + ')',
'-',
job.kind,
);
}
} catch (err) {
console.error(
'[Andela Content Script] Error extracting a job card:',
err,
);
}
});
console.log(
'[Andela Content Script] Extracted ' + jobs.length + ' jobs total',
);
return jobs;
}
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log('[Andela Content Script] Received message:', request);
if (request.action === 'extractJobs') {
expandAllSeeMoreButtons().then(function () {
setTimeout(function () {
try {
var jobs = extractJobsFromPage();
sendResponse({ jobs: jobs });
} catch (error) {
console.error('[Andela Content Script] Error:', error);
sendResponse({ jobs: [] });
}
}, 500);
});
return true;
}
if (request.action === 'checkLoginPage') {
var result = isLoginPage();
console.log('[Andela Content Script] Is login page:', result);
sendResponse({ isLoginPage: result });
}
if (request.action === 'enterEmailAndLogin') {
var success = enterEmailAndClickLogin(request.email);
sendResponse({ success: success });
}
if (request.action === 'extractPostedDate') {
var el = document.querySelector('span.text-success-500');
var postedText = el ? el.textContent.trim() : '';
console.log(
'[Andela Content Script] Extracted posted date text:',
postedText,
);
sendResponse({ postedDate: postedText });
}
return true;
});