lazy-apply 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/runner.js +40 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node
|
|
|
6
6
|
import { homedir, hostname, platform, tmpdir, userInfo } from "node:os";
|
|
7
7
|
import { dirname, join } from "node:path";
|
|
8
8
|
import { createInterface } from "node:readline/promises";
|
|
9
|
-
var VERSION = true ? "0.1.
|
|
9
|
+
var VERSION = true ? "0.1.5" : "dev";
|
|
10
10
|
var DEFAULT_APP = "https://lazyapply.online";
|
|
11
11
|
var HOME = join(homedir(), ".lazy-apply");
|
|
12
12
|
var CONFIG = join(HOME, "config.json");
|
package/dist/runner.js
CHANGED
|
@@ -419,6 +419,29 @@ function findEmptyRequired() {
|
|
|
419
419
|
for (const lab of labels) {
|
|
420
420
|
const raw = textOf(lab);
|
|
421
421
|
const ctl = lab.control;
|
|
422
|
+
const opt = ctl;
|
|
423
|
+
if (opt && /radio|checkbox/.test(opt.type || "") && opt.name) {
|
|
424
|
+
if (seen.has(`name:${opt.name}`)) continue;
|
|
425
|
+
seen.add(`name:${opt.name}`);
|
|
426
|
+
const group = Array.from(document.querySelectorAll(`input[name="${CSS.escape(opt.name)}"]`));
|
|
427
|
+
const required2 = group.some((g) => g.required || g.getAttribute("aria-required") === "true");
|
|
428
|
+
if (!required2 || group.some((g) => g.checked)) continue;
|
|
429
|
+
let node = opt.parentElement, question = "";
|
|
430
|
+
while (node && node !== document.body) {
|
|
431
|
+
if (group.every((g) => node.contains(g))) {
|
|
432
|
+
const q = node.querySelector(".application-label, legend, [class*=question-title], [class*=question] > div:first-child, h3, h4");
|
|
433
|
+
question = clean(textOf(q));
|
|
434
|
+
if (question) break;
|
|
435
|
+
}
|
|
436
|
+
node = node.parentElement;
|
|
437
|
+
}
|
|
438
|
+
const label2 = question || clean(raw);
|
|
439
|
+
if (label2 && !seen.has(label2)) {
|
|
440
|
+
seen.add(label2);
|
|
441
|
+
out.push(label2);
|
|
442
|
+
}
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
422
445
|
const box = lab.closest("[data-field-entry-id], fieldset, [class*=field-entry], [class*=question], [class*=field], li") || lab.parentElement;
|
|
423
446
|
if (!box) continue;
|
|
424
447
|
const required = /\*|✱/.test(raw) || /required/i.test(lab.className) || !!(ctl && (ctl.hasAttribute("required") || ctl.getAttribute("aria-required") === "true")) || !!box.querySelector("[required], [aria-required=true]");
|
|
@@ -1105,6 +1128,12 @@ async function submitGeneric(page) {
|
|
|
1105
1128
|
if (/captcha|robot|verify you are human/i.test(text) || await page.locator("iframe[src*=hcaptcha], iframe[src*=recaptcha], iframe[src*=turnstile]").count()) throw new Error("A captcha is shown. Solve it in the window, press Submit there, then use Mark as submitted here.");
|
|
1106
1129
|
throw new Error("No confirmation text after submit");
|
|
1107
1130
|
}
|
|
1131
|
+
async function dismissCookieBanner(page) {
|
|
1132
|
+
await page.waitForTimeout(800);
|
|
1133
|
+
const button = page.getByRole("button", { name: /^(dismiss|accept( all)?( cookies)?|got it|i agree|agree|ok(ay)?|allow all)$/i }).filter({ visible: true }).first();
|
|
1134
|
+
if (await button.count().catch(() => 0)) await button.click({ timeout: 3e3 }).catch(() => {
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1108
1137
|
async function fill(app) {
|
|
1109
1138
|
const notes = [];
|
|
1110
1139
|
await report(app.id, { status: "filling", notes: ["Runner picked up the application"] });
|
|
@@ -1112,6 +1141,7 @@ async function fill(app) {
|
|
|
1112
1141
|
const page = await b.newPage({ viewport: { width: 1380, height: 940 } });
|
|
1113
1142
|
try {
|
|
1114
1143
|
await page.goto(app.job.applyUrl.replace(/#app$/, ""), { waitUntil: "domcontentloaded" });
|
|
1144
|
+
await dismissCookieBanner(page);
|
|
1115
1145
|
if (app.job.board === "greenhouse") await fillGreenhouse(page, app, notes);
|
|
1116
1146
|
else await discoverAndFillGeneric(page, app, notes);
|
|
1117
1147
|
await page.waitForTimeout(800);
|
|
@@ -1225,8 +1255,16 @@ async function loop() {
|
|
|
1225
1255
|
if (inFlight.has(app.id)) continue;
|
|
1226
1256
|
inFlight.add(app.id);
|
|
1227
1257
|
try {
|
|
1228
|
-
if (app.status === "approved"
|
|
1229
|
-
|
|
1258
|
+
if (app.status === "approved") {
|
|
1259
|
+
const stale = live.get(app.id);
|
|
1260
|
+
if (stale) {
|
|
1261
|
+
await stale.page.close().catch(() => {
|
|
1262
|
+
});
|
|
1263
|
+
live.delete(app.id);
|
|
1264
|
+
log(`refilling ${app.id}: the application changed after it was filled`);
|
|
1265
|
+
}
|
|
1266
|
+
await fill(app);
|
|
1267
|
+
} else if (app.status === "submit_requested") {
|
|
1230
1268
|
await paceSubmit(app.job.applyUrl);
|
|
1231
1269
|
await submit(app);
|
|
1232
1270
|
lastSubmitAt = Date.now();
|
package/package.json
CHANGED