107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
import fs from 'fs/promises';
|
|
|
|
import axios from 'axios';
|
|
import cheerio from 'cheerio';
|
|
import Bluebird from 'bluebird';
|
|
|
|
import type Log from 'bunyan';
|
|
import type { PrismaClient, Want, User, Listing } from '@prisma/client';
|
|
|
|
import { logAxiosError } from './utils';
|
|
import { Pg } from './pg';
|
|
|
|
export default function discogs(log: Log, pg: Pg) {
|
|
const TOKEN = 'wRTXuYZGOPsIQgKRGKCAkUkidGtvpSfQgPWkYIpD';
|
|
const apiBase = 'https://api.discogs.com'
|
|
const discogsBase = 'https://www.discogs.com'
|
|
|
|
const api = axios.create({
|
|
headers: {
|
|
'Authorization': TOKEN,
|
|
// discogs only returns the first page without a desktop u/a
|
|
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0',
|
|
},
|
|
});
|
|
|
|
async function handleApiError(e) {
|
|
logAxiosError(e);
|
|
if (e.response.status === 429) {
|
|
throw new Error('rate limited');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function user(discogs: string) {
|
|
const user = await pg.userFindName(discogs);
|
|
if (user) return user;
|
|
log.debug('fetching user', discogs);
|
|
const { data } = await api.get(`${apiBase}/users/${discogs}`);
|
|
return pg.userUpsert(data.username, data.username, data);
|
|
}
|
|
|
|
async function wantlist(user: User): Promise<Want[]> {
|
|
const stored = await pg.userWants(user);
|
|
if (stored.length) return stored;
|
|
|
|
if (!user.discogs) {
|
|
log.error(user, 'attempted to fetch wantlist of non discogs user');
|
|
throw new Error('User is not associated with a discogs account');
|
|
}
|
|
|
|
return wantlistLatest(user);
|
|
}
|
|
|
|
async function wantlistLatest(user: User): Promise<Want[]> {
|
|
const { username } = user.data as { username: string };
|
|
const { data: { wants, pagination }} = await api.get(`${apiBase}/users/${username}/wants`);
|
|
return Bluebird.mapSeries(wants, async r => pg.wantUpsert(r, user));
|
|
}
|
|
|
|
async function release(id: DiscogsRelease): Promise<any> {
|
|
const { data } = await api.get(`${apiBase}/releases/${id}`);
|
|
return data;
|
|
}
|
|
|
|
async function inventory(user: string) {
|
|
const res = await api.get(`${apiBase}/users/${user}/inventory`);
|
|
return res.data.listings;
|
|
}
|
|
|
|
async function listings(want: Want) {
|
|
const res = await api.get(`https://www.discogs.com/sell/release/${want.release}?limit=250`);
|
|
const $ = cheerio.load(res.data);
|
|
const ids = $('.item_description_title[href]')
|
|
.map(function getListingId() {
|
|
const id = Number.parseInt(this.attribs.href.replace('/sell/item/', ''), 10);
|
|
return id;
|
|
})
|
|
.get();
|
|
|
|
const unlisted = await pg.listingRmUnlisted(want, ids);
|
|
if (unlisted) log.info({ wantId: want.id, unlisted: unlisted }, 'missing listings removed');
|
|
return Bluebird.mapSeries(ids, async id => listing(id, want));
|
|
}
|
|
|
|
async function listing(id, want) {
|
|
const listing = await pg.listingFind(id);
|
|
if (listing) return listing;
|
|
|
|
try {
|
|
log.debug('fetching listing', id);
|
|
const res = await api.get(`${apiBase}/marketplace/listings/${id}`);
|
|
return pg.listingUpsert(res.data, want);
|
|
} catch (e) {
|
|
return handleApiError(e);
|
|
}
|
|
}
|
|
|
|
return {
|
|
user,
|
|
wantlist,
|
|
wantlistLatest,
|
|
inventory,
|
|
listings,
|
|
listing,
|
|
release,
|
|
};
|
|
} |