import { configs } from "./config.ts" import type { VtuberRecord, VtuberResponse } from "@futureporn/types" import qs from 'qs' interface vTuberSearchQuery { url: string; name: string; } interface vTuberSearchOptions { prefer: string; } const columns = [ 'chaturbate', 'twitter', 'patreon', 'twitch', 'tiktok', 'onlyfans', 'youtube', 'fansly', 'pornhub' ]; // @see https://bobbyhadz.com/blog/javascript-remove-trailing-slash-from-string function removeTrailingSlash(url: string) { return url.replace(/\/+$/, ''); } function buildUrlSearchOrTemplate(columns: string[], url: string): string { return `(${columns.map(column => `${column}.ilike.*${url}*`).join(',')})`; } async function findVtuber(query: Partial, options?: Partial): Promise { const { url, name } = query const queryOptions: any = {} // When url argument exists, we try searching against the vtuber's platform urls if (url) queryOptions.or = buildUrlSearchOrTemplate(columns, removeTrailingSlash(url)); if (!url && !name) throw new Error(`findVtuber requires url or name passed in an object as argument.`); const fetchUrl = `${configs.postgrestUrl}/vtubers?${qs.stringify(queryOptions, { encode: false })}` console.info(fetchUrl) const fetchOptions = { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Prefer': 'return=representation', 'Authorization': `Bearer ${configs.automationUserJwt}`, } } const res = await fetch(fetchUrl, fetchOptions) if (!res.ok) { const msg = `request failed. status=${res.status}, statusText=${res.statusText}` console.error(msg) throw new Error(msg) } const json = await res.json() as VtuberResponse[] // console.info(`vtuber results as follows.`) // console.info(json) return json?.at(0)?.id || null } async function createVtuber(vtuber: Partial): Promise { const createVtuberPayload = vtuber const fetchUrl = `${configs.postgrestUrl}/vtubers` const fetchOptions = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Prefer': 'return=representation', 'Authorization': `Bearer ${configs.automationUserJwt}` }, body: JSON.stringify(createVtuberPayload) } const res = await fetch(fetchUrl, fetchOptions) if (!res.ok) { const body = await res.text() const msg = `createVtuber fetch failed. status=${res.status}, statusText=${res.statusText}, body=${body}` console.error(msg) throw new Error(msg) } const json = await res.json() as VtuberResponse[] // console.info(`createVtuber with vtuber as follows`) // console.info(vtuber) // console.info(json) const vtuberData = json[0] if (!vtuberData) throw new Error('failed to createVtuber') return vtuberData.id } export default async function findOrCreateVtuber(query: Partial, options?: Partial): Promise { if (!query) throw new Error(`findOrCreateVtuber requires an object as argument`); const { url, name } = query if (!url) throw new Error('findOrCreateVtuber was missing url which is required'); console.info(`findOrCreateVtuber. url=${url}, name=${name}`) new URL(url) // validate URL, throw if invalid const foundVtuber = await findVtuber(query) if (!foundVtuber) { console.info(`Failed to find vtuber, so we create one.`) const vtuber = await fetch(`${configs.scoutUrl}/vtuber/data?url=${url}`).then((res): Promise => res.json() as any) return createVtuber(vtuber) } else { return foundVtuber } }