import { EventEmitter } from 'node:events'; import { setTimeout } from 'timers/promises'; import type Log from 'bunyan'; import type { Want } from '@prisma/client'; import { Pg } from './pg'; import { wantDetails } from './utils'; import routerAuth from './routers/router.auth'; export enum Events { WantAdded = 'WantAdded', StartupSync = 'StartupSync', ListingsUpdated = 'ListingsUpdated', Connection = 'Connection', } export default function createEvents(log: Log, pg: Pg, discogs, offers) { const events = new EventEmitter({ captureRejections: true }); const connections = new Map(); async function populateListings(want: Want) { try { const listings = await discogs.listings(want); return listings; } catch (e) { if (e.message === 'rate limited') { log.warn('rate limited, trying again in 60s'); await setTimeout(60 * 1000); return populateListings(want); } throw e; } } events.on('error', function eventError(e) { log.error('event error', e); }); events.on(Events.WantAdded, async function wantAdded(want: Want) { log.info({ event: Events.WantAdded, ...wantDetails(want) }); const listings = await populateListings(want); log.info({ listings: listings.length, ...wantDetails(want) }, 'listings added'); events.emit(Events.ListingsUpdated); }); events.on(Events.StartupSync, async function wantAdded() { const wants = await pg.wantsToSync(); log.info({ event: Events.StartupSync, wants: wants.length }); for (const want of wants) { const listings = await populateListings(want); log.info({ listings: listings.length, ...wantDetails(want) }, 'listings added'); } log.info({ wants: wants.length }, 'finished syncing'); events.emit(Events.ListingsUpdated); }); events.on(Events.ListingsUpdated, async function listingsUpdated() { const bundles = await offers.bundles(); log.info({ event: Events.ListingsUpdated, bundles: bundles.length }); // const bundle = { // username: b[0]?.data.seller.username, // seller: b[0]?.data.seller.id, // shipsFrom: b[0]?.data.ships_from, // total, // currency: b[0]?.data.price.currency, // shipping: '...', // items, // mentionsAustralia: shipping.toLowerCase().includes('australia'), // mentionsWorldwide: shipping.toLowerCase().includes('world'), // } for (const bundle of bundles) { log.info({ seller: bundle.seller, items: bundle.items.length }, 'generating offers...'); const listingIds = bundle.items.map(i => i.id); const updatedOffers = await offers.generate(listingIds, 100, 30) log.info({ offers: updatedOffers.length }, 'offers set'); } log.info('offers up to date'); }); events.on(Events.Connection, async function onConnection({ ws, id }) { connections.set(id, ws); }) return events; }