Files
scan-receipts/scripts/fast_multi_country.mjs
Timo 84b9987c49 Add full application: receipt scanning, auth, billing, and account deletion
Brings the working codebase (Next.js app, auth system, Stripe billing,
Docker/deploy config, tests, docs) into version control on top of the
placeholder initial commit, and adds account self-deletion (Danger Zone
in Settings, password + typed-email confirmation, cascading DB cleanup,
Stripe cancellation) per GDPR right-to-erasure.

Excludes local build caches, node_modules, and internal agent scratch
files; .gitignore hardened to keep those out going forward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 20:59:04 +02:00

84 lines
2.0 KiB
JavaScript

const EN_KEYWORDS = [
'receipt scanner',
'receipt to excel',
'receipt scanner to excel',
'expense tracker',
'receipt keeper',
'invoice scanner',
'tax receipts',
'ocr receipt scanner',
'smart receipts',
'mileage and receipts',
'receipt organizer',
'bookkeeping scanner',
'scan receipts for taxes',
'extract receipt to csv',
'business expense tracker'
];
const DE_KEYWORDS = [
'beleg scanner',
'belege digitalisieren',
'rechnung scanner',
'kassenbon scanner',
'quittung scanner',
'spesen app',
'buchhaltung scanner',
'datev scanner',
'receipt to excel',
'haushaltsbuch',
'ausgaben tracker',
'steuer belege',
'belegmanager'
];
const COUNTRIES = {
US: 'us',
GB: 'gb',
CA: 'ca',
AU: 'au',
DE: 'de'
};
async function fetchSearch(term, country) {
try {
const url = `https://itunes.apple.com/search?term=${encodeURIComponent(term)}&country=${country}&entity=software&limit=5`;
const res = await fetch(url, { signal: AbortSignal.timeout(3000) });
if (!res.ok) return [];
const data = await res.json();
return data.results || [];
} catch {
return [];
}
}
async function run() {
const summary = {};
for (const [cName, cCode] of Object.entries(COUNTRIES)) {
summary[cName] = {};
const kws = cName === 'DE' ? DE_KEYWORDS : EN_KEYWORDS;
// run in chunks of 5
for (let i = 0; i < kws.length; i += 5) {
const chunk = kws.slice(i, i + 5);
await Promise.all(chunk.map(async (kw) => {
const apps = await fetchSearch(kw, cCode);
const top5Reviews = apps.reduce((sum, a) => sum + (a.userRatingCount || 0), 0);
const avgReviews = Math.round(top5Reviews / (apps.length || 1));
const top1 = apps[0] || {};
summary[cName][kw] = {
count: apps.length,
top1: top1.trackName ? `${top1.trackName.slice(0, 30)} (${top1.userRatingCount || 0} rev)` : 'None',
avgReviews
};
}));
}
}
console.log(JSON.stringify(summary, null, 2));
}
run();