Files
stef-openclaw-skills/skills/amazon-shopping/src/report.ts
T

132 lines
4.9 KiB
TypeScript

import type { ProductFilters, ProductSearchResult, SearchProductsResponse } from "./types.js";
import { extractWidthInches, formatWidthInches } from "./product-metrics.js";
export interface ResponseInput {
query: string;
filters: ProductFilters;
limit: number;
maxSearchPages: number;
results: ProductSearchResult[];
filteredOutCount: number;
warnings: string[];
now?: () => Date;
}
export function createResponse(input: ResponseInput): SearchProductsResponse {
return {
query: input.query,
filters: input.filters,
limit: input.limit,
maxSearchPages: input.maxSearchPages,
results: input.results,
filteredOutCount: input.filteredOutCount,
warnings: input.warnings,
source: {
site: "amazon.com",
scrapedAt: (input.now ?? (() => new Date()))().toISOString(),
automation: "web-automation/CloakBrowser"
}
};
}
function formatFilters(filters: ProductFilters): string {
const parts = [
filters.minRating !== undefined ? `rating ${filters.ratingComparison ?? "gte"} ${filters.minRating}` : "",
filters.minReviews !== undefined ? `reviews ${filters.reviewCountComparison ?? "gte"} ${filters.minReviews}` : "",
filters.maxPrice !== undefined ? `price <= $${filters.maxPrice}` : "",
filters.maxUnitPrice !== undefined ? `unit price <= $${filters.maxUnitPrice}` : "",
filters.minWidthInches !== undefined ? `width ${filters.widthComparison ?? "gte"} ${filters.minWidthInches} inches` : "",
filters.requirePrime ? "Prime delivery" : "",
filters.requireFreeDelivery ? "free delivery" : "",
filters.deliveryBy ? `delivery by ${filters.deliveryBy}` : "",
filters.sortBy === "price" ? "sort by price" : ""
].filter(Boolean);
return parts.length > 0 ? parts.join(", ") : "none";
}
function compactText(value: string): string {
return value.replace(/\s+/g, " ").trim();
}
function marker(passes: boolean | undefined, enabled: boolean): string {
if (!enabled) {
return "";
}
return passes ? " OK" : " NO";
}
function widthCell(product: ProductSearchResult, filters: ProductFilters): string {
const width = extractWidthInches(product);
const passes = width !== undefined && (filters.widthComparison === "gt" ? width > (filters.minWidthInches ?? 0) : width >= (filters.minWidthInches ?? 0));
return `${formatWidthInches(width)}${marker(passes, filters.minWidthInches !== undefined)}`;
}
function primeCell(product: ProductSearchResult, filters: ProductFilters): string {
if (product.delivery?.prime) {
return `Prime${marker(true, Boolean(filters.requirePrime))}`;
}
return `not verified${marker(false, Boolean(filters.requirePrime))}`;
}
function deliveryCell(product: ProductSearchResult, filters: ProductFilters): string {
const display = product.delivery?.display ?? "unknown";
if (!filters.deliveryBy) {
return display;
}
const normalized = display.toLowerCase();
const passes = filters.deliveryBy === "today"
? /\btoday\b|same[- ]day/.test(normalized)
: filters.deliveryBy === "tomorrow" || filters.deliveryBy === "overnight"
? /\btomorrow\b|overnight|next[- ]day|one[- ]day/.test(normalized)
: normalized.includes(filters.deliveryBy.toLowerCase());
return `${display}${marker(passes, true)}`;
}
function resultBlocks(products: ProductSearchResult[], filters: ProductFilters): string[] {
return products.flatMap((product, index) => [
`${index + 1}. ${compactText(product.title)}`,
`Price: ${product.price?.display ?? "unknown"}`,
`Rating: ${product.rating ?? "unknown"} stars`,
`Reviews: ${product.reviewCount?.toLocaleString("en-US") ?? "unknown"}`,
`Width: ${widthCell(product, filters)}`,
`Prime: ${primeCell(product, filters)}`,
`Delivery: ${compactText(deliveryCell(product, filters))}`,
`Link: ${product.url}`,
""
]);
}
function metadataLines(products: ProductSearchResult[]): string[] {
const lines: string[] = [];
for (const product of products) {
const notes = [
product.missingFields.length > 0 ? `missing ${product.missingFields.join(", ")}` : "",
product.isSponsored ? "sponsored" : "",
product.extractionNotes.length > 0 ? product.extractionNotes.join("; ") : ""
].filter(Boolean);
if (notes.length > 0) {
lines.push(`- ${product.title}: ${notes.join("; ")}`);
}
}
return lines;
}
export function createMarkdownReport(response: SearchProductsResponse): string {
const lines = [
`# Amazon Shopping Results`,
"",
`Query: ${response.query}`,
`Filters: ${formatFilters(response.filters)}`,
`Results returned: ${response.results.length} (filtered out: ${response.filteredOutCount})`,
response.warnings.length > 0 ? `Warnings: ${response.warnings.join("; ")}` : "",
"",
"## Best Matches",
"",
response.results.length > 0 ? "" : "No products matched all requested filters.",
...resultBlocks(response.results, response.filters),
"",
...metadataLines(response.results)
].filter((line) => line !== "");
return `${lines.join("\n")}\n`;
}