ozon-grabber 1.0.1 → 1.1.0
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/index.js +42 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { join } from "path";
|
|
4
6
|
import yargs from "yargs";
|
|
5
7
|
import { hideBin } from "yargs/helpers";
|
|
6
8
|
|
|
@@ -322,6 +324,17 @@ var normalizeStatus = (rawText) => {
|
|
|
322
324
|
}
|
|
323
325
|
return "in progress";
|
|
324
326
|
};
|
|
327
|
+
var normalizeProductHref = (href) => {
|
|
328
|
+
if (!href) return null;
|
|
329
|
+
const trimmed = href.trim();
|
|
330
|
+
if (!trimmed) return null;
|
|
331
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
332
|
+
if (!/^https?:\/\/(www\.)?ozon\.ru\//i.test(trimmed)) return null;
|
|
333
|
+
return trimmed;
|
|
334
|
+
}
|
|
335
|
+
if (trimmed.startsWith("/")) return `https://www.ozon.ru${trimmed}`;
|
|
336
|
+
return null;
|
|
337
|
+
};
|
|
325
338
|
var deduplicateItems = (items) => {
|
|
326
339
|
const seen = /* @__PURE__ */ new Set();
|
|
327
340
|
return items.filter((item) => {
|
|
@@ -360,6 +373,10 @@ var pickImageUrl = (root) => {
|
|
|
360
373
|
if (!img) return null;
|
|
361
374
|
return img.getAttribute("src") ?? null;
|
|
362
375
|
};
|
|
376
|
+
var pickProductUrl = (root) => {
|
|
377
|
+
const link = root.querySelector(PRODUCT_LINK);
|
|
378
|
+
return normalizeProductHref(link?.getAttribute("href"));
|
|
379
|
+
};
|
|
363
380
|
var pickShipmentStatus = (shipment) => {
|
|
364
381
|
const statusEls = Array.from(shipment.querySelectorAll(SHIPMENT_STATUS));
|
|
365
382
|
if (statusEls.length === 0) return "unknown";
|
|
@@ -385,6 +402,7 @@ var extractOrderItems = (root) => {
|
|
|
385
402
|
title,
|
|
386
403
|
price,
|
|
387
404
|
imageUrl: pickImageUrl(itemRoot),
|
|
405
|
+
productUrl: pickProductUrl(itemRoot),
|
|
388
406
|
quantity,
|
|
389
407
|
status
|
|
390
408
|
});
|
|
@@ -440,6 +458,7 @@ var extractReturnItems = (root) => {
|
|
|
440
458
|
title,
|
|
441
459
|
price,
|
|
442
460
|
imageUrl: findImageNearby(link),
|
|
461
|
+
productUrl: normalizeProductHref(link.getAttribute("href")),
|
|
443
462
|
quantity,
|
|
444
463
|
status: "returned"
|
|
445
464
|
});
|
|
@@ -538,6 +557,8 @@ async function scanOrders(conn, userId, startOrder, options) {
|
|
|
538
557
|
const orders = [];
|
|
539
558
|
let stopReason = "max-orders";
|
|
540
559
|
let stopOrderNumber = startOrder;
|
|
560
|
+
const MISS_TOLERANCE = 3;
|
|
561
|
+
let consecutiveMisses = 0;
|
|
541
562
|
for (let i = 0; i < maxOrders; i++) {
|
|
542
563
|
const orderNumber = isReturns ? `R${startNum + i}` : formatOrderNumber(startNum + i, width);
|
|
543
564
|
const orderId = `${userId}-${orderNumber}`;
|
|
@@ -548,19 +569,32 @@ async function scanOrders(conn, userId, startOrder, options) {
|
|
|
548
569
|
await openPage(conn, url);
|
|
549
570
|
} catch (err) {
|
|
550
571
|
if (verbose) logger(` Error opening page: ${err instanceof Error ? err.message : String(err)}`);
|
|
551
|
-
|
|
572
|
+
consecutiveMisses += 1;
|
|
552
573
|
stopOrderNumber = orderNumber;
|
|
553
|
-
|
|
574
|
+
if (consecutiveMisses >= MISS_TOLERANCE) {
|
|
575
|
+
if (verbose)
|
|
576
|
+
logger(` ${MISS_TOLERANCE} consecutive misses, stopping.`);
|
|
577
|
+
stopReason = "missing";
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
continue;
|
|
554
581
|
}
|
|
555
582
|
const waitSelector = isReturns ? RETURN_DETAILS_WIDGET : SHIPMENT_WIDGET;
|
|
556
583
|
if (verbose) logger(` Waiting for ${waitSelector}...`);
|
|
557
584
|
const found = await waitForSelector(conn, waitSelector, pageLoadTimeoutMs);
|
|
558
585
|
if (!found) {
|
|
559
|
-
if (verbose) logger(` Widget not found for ${orderNumber}
|
|
560
|
-
|
|
586
|
+
if (verbose) logger(` Widget not found for ${orderNumber} (gap?)`);
|
|
587
|
+
consecutiveMisses += 1;
|
|
561
588
|
stopOrderNumber = orderNumber;
|
|
562
|
-
|
|
589
|
+
if (consecutiveMisses >= MISS_TOLERANCE) {
|
|
590
|
+
if (verbose)
|
|
591
|
+
logger(` ${MISS_TOLERANCE} consecutive misses, stopping.`);
|
|
592
|
+
stopReason = "missing";
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
continue;
|
|
563
596
|
}
|
|
597
|
+
consecutiveMisses = 0;
|
|
564
598
|
if (!isReturns) {
|
|
565
599
|
await waitForSelector(conn, TITLE_WITH_TIMER_WIDGET, 5e3);
|
|
566
600
|
}
|
|
@@ -689,11 +723,12 @@ function createOnOrderCallback(opts) {
|
|
|
689
723
|
}
|
|
690
724
|
|
|
691
725
|
// src/index.ts
|
|
726
|
+
var DEFAULT_USER_DATA_DIR = join(homedir(), ".ozon_chrome_profile");
|
|
692
727
|
var userDataDirOption = {
|
|
693
728
|
"user-data-dir": {
|
|
694
729
|
type: "string",
|
|
695
|
-
default:
|
|
696
|
-
describe: "Chrome profile directory (with Ozon session)"
|
|
730
|
+
default: DEFAULT_USER_DATA_DIR,
|
|
731
|
+
describe: "Chrome profile directory (with Ozon session). Defaults to ~/.ozon_chrome_profile."
|
|
697
732
|
}
|
|
698
733
|
};
|
|
699
734
|
yargs(hideBin(process.argv)).parserConfiguration({ "parse-numbers": false }).command(
|
package/package.json
CHANGED