@accounter/scraper-app
Version:
Scraper app with Fastify server and React UI
70 lines (61 loc) • 2.14 kB
text/typescript
import { addMonths, format, startOfMonth } from 'date-fns';
import type { z } from 'zod';
import { init } from '@accounter/modern-poalim-scraper';
import type { ServerMessage } from '../../shared/ws-protocol.js';
import type { DiscountPayload } from '../payload-schemas/discount.schema.js';
import { validatePayload } from '../validate-payload.js';
import type { DiscountAccountSchema } from '../vault.js';
export type DiscountCreds = z.infer<typeof DiscountAccountSchema>;
export type Emitter = (msg: ServerMessage) => void;
function buildMonthList(dateFrom: Date, dateTo: Date): Date[] {
const months: Date[] = [];
let current = startOfMonth(dateFrom);
const end = startOfMonth(dateTo);
while (current <= end) {
months.push(current);
current = addMonths(current, 1);
}
return months;
}
export async function scrapeDiscount(
creds: DiscountCreds,
dateFrom: Date,
dateTo: Date,
emit: Emitter,
headless = true,
): Promise<DiscountPayload> {
const { discount: discountFn, close } = await init({ headless });
try {
const scraper = await discountFn({
ID: creds.ID,
password: creds.password,
...(creds.code ? { code: creds.code } : {}),
});
const months = buildMonthList(dateFrom, dateTo);
const results: DiscountPayload = [];
for (const month of months) {
const monthStr = format(month, 'yyyy-MM');
emit({ type: 'task-month-fetching', sourceId: creds.id, month: monthStr });
try {
const { accountNumber, balance, transactions } = await scraper.getMonthTransactions(month);
emit({
type: 'task-month-fetched',
sourceId: creds.id,
month: monthStr,
transactionCount: transactions.length,
});
results.push({ accountNumber, month: monthStr, balance, transactions });
} catch (err) {
emit({
type: 'task-month-error',
sourceId: creds.id,
month: monthStr,
error: err instanceof Error ? err.message : String(err),
});
}
}
return validatePayload('discount', results);
} finally {
await close();
}
}