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.
Files changed (2) hide show
  1. package/dist/index.js +42 -7
  2. 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
- stopReason = "missing";
572
+ consecutiveMisses += 1;
552
573
  stopOrderNumber = orderNumber;
553
- break;
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}, stopping.`);
560
- stopReason = "missing";
586
+ if (verbose) logger(` Widget not found for ${orderNumber} (gap?)`);
587
+ consecutiveMisses += 1;
561
588
  stopOrderNumber = orderNumber;
562
- break;
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: "./chrome-profile",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ozon-grabber",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "CLI for scanning Ozon order/return history via a real Chrome session and submitting parsed data to a local SQLite-backed service.",
6
6
  "license": "MIT",