Skip to content

Instantly share code, notes, and snippets.

@izelnakri
Last active September 8, 2022 15:40
Show Gist options
  • Save izelnakri/096c92e661a9f3dc69c99e24ed8747bf to your computer and use it in GitHub Desktop.
Save izelnakri/096c92e661a9f3dc69c99e24ed8747bf to your computer and use it in GitHub Desktop.
Running this as a local cronjob with 1h intervals
import process from "process";
import chrome from 'chrome-cookies-secure';
import Puppeteer from "puppeteer";
const HOURLY_RATE = process.env.HOURLY_RATE || 320;
const DEBUG = process.env.DEBUG;
const CHROME_PROFILE = 'Profile 2'; // Change this to your chrome profile
const DEFAULT_TIMEOUT = 3000;
const BROWSER_HEIGHT = 800;
const BROWSER_WIDTH = 1200;
const BROWSER_DATA_PATH = '/home/izelnakri/.config/google-chrome';
const BROWSER_BIN_PATH = '/usr/sbin/google-chrome-stable';
const RECRUITER_TITLE_TOKENS = ['recruit', 'Sourcer', 'talent', 'human resource'];
const RECRUITER_MESSAGE_TOKENS = [
'🍪', 'role', 'team', 'join', 'attrition', 'working hour', '100% remote', 'promising', 'freelance', 'scrum',
'requirement', 'agile', 'product manager', 'product owner', 'product design', 'product development', 'salary'
];
(async () => {
console.log((new Date()).toString(), 'Running dear-recruiter.js:');
const cookies = await chrome.getCookiesPromised('https://linkedin.com/messaging/', 'puppeteer', CHROME_PROFILE);
let browser = await Puppeteer.launch({
executablePath: BROWSER_BIN_PATH,
userDataDir: BROWSER_DATA_PATH,
headless: !DEBUG,
args: [`--window-size=${BROWSER_WIDTH},${BROWSER_HEIGHT}`],
});
const page = await browser.newPage()
await page.setCookie(...cookies);
await page.setViewport({ width: BROWSER_WIDTH, height: BROWSER_HEIGHT });
await page.goto('https://www.linkedin.com/messaging', { timeout: 0 });
await page.waitForTimeout(DEFAULT_TIMEOUT);
page.on('console', async (msg) => {
const args = await Promise.all(msg.args().map((arg) => turnToObjects(arg)));
console.log(...args);
});
let sentMessages = await page.evaluate(async ([DEFAULT_TIMEOUT, RECRUITER_TITLE_TOKENS, RECRUITER_MESSAGE_TOKENS, HOURLY_RATE]) => {
const LAST_CONVERSATIONS_CARDS = Array.from(document.querySelectorAll('.msg-conversation-card__message-snippet'))
// .filter((conversation) => RECRUITER_MESSAGE_TOKENS.some((token) => conversation.textContent.includes(token)));
let result = [];
for (const [index, conversationCard] of LAST_CONVERSATIONS_CARDS.entries()) {
if (index <= 4) {
result.push(await checkConversation(conversationCard));
}
}
return result;
async function checkConversation(conversationCard) {
conversationCard.click();
await wait(DEFAULT_TIMEOUT);
window.scroll({ top: 220 })
let userFullName = document.querySelector('#thread-detail-jump-target')?.textContent.trim();
let userTitle = document.querySelector('.artdeco-entity-lockup__subtitle')?.textContent.trim();
let messageHistory = Array.from(document.querySelectorAll('.msg-s-event-listitem__body'));
let firstMessage = messageHistory[0] && messageHistory[0].textContent.trim();
if (messageHistory.length === 1 && firstMessage && isRecruiter(userTitle, firstMessage)) {
let message = letsCutTheBullshit(userFullName, HOURLY_RATE);
let messageForm = document.querySelector('.msg-form__contenteditable');
if (await fillIn(messageForm, message)) {
await wait(DEFAULT_TIMEOUT);
let sendButton = document.querySelector('.msg-form__send-button');
sendButton.disabled = false
sendButton.click();
await wait(DEFAULT_TIMEOUT);
let contactSharePopup = document.querySelector('.artdeco-modal-is-open');
if (contactSharePopup) {
let notNowButton = contactSharePopup.querySelector('.artdeco-button--muted');
notNowButton.click();
}
return [userFullName, userTitle, message, firstMessage];
}
}
}
function wait(timeout) {
return new Promise((resolve) => setTimeout(resolve, timeout));
}
function isRecruiter(userTitle, message) {
let sanitezedUserTitle = userTitle ? userTitle.toLowerCase() : '';
return RECRUITER_TITLE_TOKENS.some((token) => sanitezedUserTitle.includes(token))
|| RECRUITER_MESSAGE_TOKENS.some((token) => message.includes(token));
}
function letsCutTheBullshit(recruiterName, hourlyRate) {
// NOTE: This is partially written by GitHub Copilot:
return 'Hi ' + recruiterName.split(' ')[0] + ',\n\nI\'m flattered by your interest.'
+ 'However I\'d be only interested in a job that pays at least $' + hourlyRate + '/hr, given my experience and '
+ 'current demand in the market. I hope you understand it. Please only contact me if you think my rate is fair.'
+ '\n\nBest,\nIzel';
}
function fillIn(element, text) {
return new Promise((resolve) => {
setTimeout(() => {
try {
element.blur()
element.focus();
document.execCommand('insertText', false, text);
resolve(true);
} catch (error) {
resolve(false);
}
}, 1000);
});
}
}, [DEFAULT_TIMEOUT, RECRUITER_TITLE_TOKENS, RECRUITER_MESSAGE_TOKENS, HOURLY_RATE]);
sentMessages.forEach((message) => {
if (Array.isArray(message)) {
console.log('====================================');
console.log('Message sent to:', message[0]);
console.log('Title:', message[1]);
console.log('Message:', message[2]);
console.log('In response to:\n\n', message[3]);
console.log('====================================');
}
});
console.log((new Date()).toString(), 'Closing the browser on:');
console.log(page.url());
browser.close()
process.exit(0);
})();
function turnToObjects(jsHandle) {
return jsHandle.jsonValue();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment