← back to Govarbitrage
src/lib/marketplace-links.ts
139 lines
// Build deep-search links to (a) other auction/resale marketplaces and (b)
// social "goods-needed"/flipping boards, from a listing's identifying terms.
// All $0, no API keys — each link opens the network's live results for the item.
export interface LinkChip {
label: string;
url: string;
group: "auction" | "resale" | "retail" | "board";
}
interface Named {
title: string;
manufacturer?: string | null;
model?: string | null;
category?: string | null;
}
/** Best short search query for an item: prefer brand+model, strip lot/qty noise. */
export function searchQuery(l: Named): string {
const generic = /^(assorted|mixed|various|lot|n\/a|unknown|misc)/i;
const parts: string[] = [];
if (l.manufacturer && !generic.test(l.manufacturer)) parts.push(l.manufacturer);
if (l.model && !generic.test(l.model)) parts.push(l.model);
let q = parts.join(" ").trim();
if (!q) q = l.title;
// Strip lot/quantity noise: "(4 EA)", "Lot of 12", "1 LOT", trailing counts.
q = q
.replace(/\([^)]*\)/g, " ")
.replace(/\b(lot of|qty|quantity)\b\s*\d*/gi, " ")
.replace(/\b\d+\s*(ea|each|lot|pcs?|units?)\b/gi, " ")
.replace(/\bDEMIL\b/gi, " ")
.replace(/[^\w\s.\-/]/g, " ")
.replace(/\s+/g, " ")
.trim();
return q.split(/\s+/).slice(0, 7).join(" ");
}
const enc = (s: string) => encodeURIComponent(s);
/** Actual offers/comps on other auction + resale + retail networks. */
export function auctionNetworkLinks(l: Named): LinkChip[] {
const q = searchQuery(l);
const e = enc(q);
const links: LinkChip[] = [
{ label: "eBay Sold", url: `https://www.ebay.com/sch/i.html?_nkw=${e}&LH_Sold=1&LH_Complete=1`, group: "resale" },
{ label: "eBay Active", url: `https://www.ebay.com/sch/i.html?_nkw=${e}`, group: "resale" },
{ label: "GovDeals", url: `https://www.govdeals.com/search?kWord=${e}`, group: "auction" },
{ label: "AllSurplus", url: `https://www.allsurplus.com/search?query=${e}`, group: "auction" },
{ label: "Public Surplus", url: `https://www.publicsurplus.com/sms/browse/search?keyword=${e}`, group: "auction" },
{ label: "Municibid", url: `https://municibid.com/Browse?search=${e}`, group: "auction" },
{ label: "Google Shopping", url: `https://www.google.com/search?tbm=shop&q=${e}`, group: "retail" },
];
// Category-specialist venues.
const cat = `${l.category ?? ""} ${l.title}`.toLowerCase();
if (/forklift|generator|welder|lathe|cnc|scissor|mower|tractor|compressor|industrial|machin/.test(cat)) {
links.push({ label: "MachineryTrader", url: `https://www.machinerytrader.com/listings/search?keywords=${e}`, group: "resale" });
}
if (/lab|microscope|centrifuge|analyzer|hplc|spectro|scientific/.test(cat)) {
links.push({ label: "LabX", url: `https://www.labx.com/search?q=${e}`, group: "resale" });
}
if (/medical|dental|patient|exam|surgical|hospital|stretcher|vital/.test(cat)) {
links.push({ label: "DOTmed", url: `https://www.dotmed.com/equipment/search?q=${e}`, group: "resale" });
}
return links;
}
export interface RegionLinks {
region: string;
links: LinkChip[];
}
/** International resale + auction venues to check an item's price abroad. */
export function internationalNetworkLinks(l: Named): RegionLinks[] {
const e = enc(searchQuery(l));
return [
{
region: "Europe",
links: [
{ label: "eBay UK", url: `https://www.ebay.co.uk/sch/i.html?_nkw=${e}`, group: "resale" },
{ label: "eBay Germany", url: `https://www.ebay.de/sch/i.html?_nkw=${e}`, group: "resale" },
{ label: "Catawiki", url: `https://www.catawiki.com/en/s?q=${e}`, group: "auction" },
{ label: "Troostwijk", url: `https://www.troostwijkauctions.com/en/l?searchQuery=${e}`, group: "auction" },
],
},
{
region: "Japan",
links: [
{ label: "Yahoo! Auctions JP", url: `https://auctions.yahoo.co.jp/search/search?p=${e}`, group: "resale" },
{ label: "Mercari JP", url: `https://jp.mercari.com/search?keyword=${e}`, group: "resale" },
],
},
{
region: "Hong Kong",
links: [{ label: "Carousell HK", url: `https://www.carousell.com.hk/search/${e}`, group: "resale" }],
},
{
region: "Australia",
links: [
{ label: "GraysOnline", url: `https://www.graysonline.com/search?keywords=${e}`, group: "auction" },
{ label: "Pickles", url: `https://www.pickles.com.au/search?q=${e}`, group: "auction" },
{ label: "eBay AU", url: `https://www.ebay.com.au/sch/i.html?_nkw=${e}`, group: "resale" },
],
},
{
region: "China",
links: [
{ label: "Taobao", url: `https://s.taobao.com/search?q=${e}`, group: "resale" },
{ label: "JD Auction", url: `https://auction.jd.com/searchList.html?keyword=${e}`, group: "auction" },
{ label: "Alibaba", url: `https://www.alibaba.com/trade/search?SearchText=${e}`, group: "retail" },
],
},
];
}
/** Flat top-N international chips (for the compact dashboard column). */
export function internationalTopLinks(l: Named): LinkChip[] {
const e = enc(searchQuery(l));
return [
{ label: "eBay UK", url: `https://www.ebay.co.uk/sch/i.html?_nkw=${e}`, group: "resale" },
{ label: "Yahoo! JP", url: `https://auctions.yahoo.co.jp/search/search?p=${e}`, group: "resale" },
{ label: "Grays AU", url: `https://www.graysonline.com/search?keywords=${e}`, group: "auction" },
{ label: "Taobao CN", url: `https://s.taobao.com/search?q=${e}`, group: "resale" },
];
}
/** Social "goods-needed"/flipping/ISO boards + local marketplaces. */
export function boardLinks(l: Named): LinkChip[] {
const q = searchQuery(l);
const e = enc(q);
return [
{ label: "Reddit", url: `https://www.reddit.com/search/?q=${e}`, group: "board" },
{ label: "r/Flipping", url: `https://www.reddit.com/r/Flipping/search/?q=${e}&restrict_sr=1`, group: "board" },
{ label: "Reddit ISO/Wanted", url: `https://www.reddit.com/search/?q=${enc(`${q} ISO OR wanted OR "looking for"`)}`, group: "board" },
{ label: "Facebook Marketplace", url: `https://www.facebook.com/marketplace/search/?query=${e}`, group: "board" },
{ label: "Craigslist", url: `https://www.craigslist.org/search/sss?query=${e}`, group: "board" },
{ label: "Discord (Disboard)", url: `https://disboard.org/search?keyword=${e}`, group: "board" },
];
}