Files
l2-market-parser/src/parser.js
T

129 lines
3.4 KiB
JavaScript

export function rectFromPoints(start, end) {
return {
x: Math.round(Math.min(start.x, end.x)),
y: Math.round(Math.min(start.y, end.y)),
width: Math.round(Math.abs(end.x - start.x)),
height: Math.round(Math.abs(end.y - start.y)),
};
}
export function resolveFieldRect(anchorPosition, field) {
return {
x: Math.round(anchorPosition.x + field.offsetX),
y: Math.round(anchorPosition.y + field.offsetY),
width: Math.round(field.width),
height: Math.round(field.height),
};
}
export function combineRects(first, second) {
const x = Math.min(first.x, second.x);
const y = Math.min(first.y, second.y);
return {
x,
y,
width: Math.max(first.x + first.width, second.x + second.width) - x,
height: Math.max(first.y + first.height, second.y + second.height) - y,
};
}
export function normalizeOcrText(text) {
return text.replace(/\s+/g, " ").trim();
}
export function parseFieldValue(type, text) {
const normalized = normalizeOcrText(text);
if (type !== "price" && type !== "quantity") {
return normalized;
}
const digits = normalized.replace(/\D/g, "");
return digits ? Number(digits) : null;
}
export function parseStoreHeaderText(text) {
const normalized = normalizeOcrText(text);
const match = normalized.match(
/private\s*store.*?\b(buy|sell)\b.*?[-–—]\s*(.+)$/i,
);
if (!match?.[2]?.trim()) return null;
return {
side: match[1].toLowerCase(),
merchant: match[2].trim(),
rawText: normalized,
};
}
export function parseTooltipText(text) {
const lines = text
.split(/\r?\n/)
.map(normalizeOcrText)
.filter(Boolean);
const priceIndex = lines.findIndex(
(line) =>
/\d[\d\s,.'`]*\s*adena/i.test(line) ||
/pr[i1l]ce\s*[:;]?\s*\d/i.test(line) ||
/^\s*\d[\d\s,.'`]*\s*$/.test(line),
);
if (priceIndex < 0) return null;
const priceLine = lines[priceIndex];
const priceText =
priceLine.match(/([\d][\d\s,.'`]*)\s*adena/i)?.[1] ??
priceLine.match(/pr[i1l]ce\s*[:;]?\s*([\d][\d\s,.'`]*)/i)?.[1] ??
priceLine.match(/^\s*([\d][\d\s,.'`]*)\s*$/)?.[1] ??
"";
const digits = priceText.replace(/\D/g, "");
let name = lines.slice(0, priceIndex).find((line) => !/^pr[i1l]ce\s*[:;]?$/i.test(line)) ?? "";
if (!name) {
name = priceLine.slice(0, priceLine.search(/pr[i1l]ce/i)).trim();
}
if (!name || !digits) return null;
const quantityMatches = [...name.matchAll(/\(([\d][\d\s,.]*)\)/g)];
const quantityMatch = quantityMatches.at(-1);
const quantityText = quantityMatch?.[1].replace(/\D/g, "") ?? "";
if (quantityMatch) {
name = `${name.slice(0, quantityMatch.index)} ${name.slice(quantityMatch.index + quantityMatch[0].length)}`
.replace(/\s+/g, " ")
.trim();
}
return {
name,
quantity: quantityText ? Number(quantityText) : 1,
priceAdena: Number(digits),
rawText: lines.join("\n"),
};
}
export function isCalibrationReady(calibration) {
return Boolean(
calibration?.saleAnchor?.image &&
calibration.saleAnchor.width >= 4 &&
calibration.saleAnchor.height >= 4 &&
calibration.storeHeaderRegion &&
calibration.tooltipSearchRegion &&
calibration.tooltipAnchor?.image &&
calibration.itemNameRegion &&
calibration.itemPriceRegion,
);
}
export function isRectInside(rect, width, height) {
return (
rect.x >= 0 &&
rect.y >= 0 &&
rect.width > 0 &&
rect.height > 0 &&
rect.x + rect.width <= width &&
rect.y + rect.height <= height
);
}