import got from "got"; import config from "../../config"; import fs from "fs/promises"; import path from "path"; export interface GetPostQuery { tags: readonly string[]; maxPage: number; } export interface Post { id: number; file: { url: string; }; sources: readonly string[]; tags: { general: readonly string[]; species: readonly string[]; character: readonly string[]; copyright: readonly string[]; artist: readonly string[]; invalid: readonly string[]; lore: readonly string[]; meta: readonly string[]; }; } export const client = got.extend({ headers: { "User-Agent": config.e621.userAgent, }, }); const dedupePath = path.join(__dirname, "e926dedupe.json"); const dedupeMax = 50; let dedupeDb: number[] | undefined; async function loadDedupeDb() { if (dedupeDb) { return; } try { await fs.stat(dedupePath); } catch { await saveDedupeDb(); } const d = await fs.readFile(dedupePath, "utf8"); const dd = JSON.parse(d); if (dd instanceof Array) { dedupeDb = dd.slice(0, -1 * dedupeMax); } else { dedupeDb = []; } } async function saveDedupeDb() { await fs.writeFile(dedupePath, JSON.stringify(dedupeDb ?? []), "utf8"); } export async function getPostById(id: number) { const response = await client .get("https://e926.net/posts.json", { searchParams: { tags: `id:${id}`, }, }) .json<{ posts: readonly Post[] }>(); if (!response.posts.length) { throw new Error("No posts received"); } return response.posts[0]; } export async function getRandomPost(query: GetPostQuery) { await loadDedupeDb(); const page = Math.floor(Math.random() * (query.maxPage - 1)) + 1; const response = await client .get("https://e926.net/posts.json", { searchParams: { limit: 75, page, tags: query.tags.join(" "), }, }) .json<{ posts: readonly Post[] }>(); if (!response.posts.length) { throw new Error("No posts received"); } const postIndex = Math.floor(Math.random() * response.posts.length); const post = response.posts[postIndex]; if (dedupeDb.includes(post.id)) { return getRandomPost(query); } dedupeDb.push(post.id); await saveDedupeDb(); return post; }