123 lines
No EOL
5.2 KiB
TypeScript
123 lines
No EOL
5.2 KiB
TypeScript
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
|
import desc from './package.json';
|
|
import secrets from "@/bun/api/secrets";
|
|
import PQueue from 'p-queue';
|
|
import * as igdb from '@phalcode/ts-igdb-client';
|
|
import { checkLoginAndRefreshTwitch } from "@/bun/api/auth";
|
|
import { GameLookup } from "@/shared/types";
|
|
|
|
export default class IgdbIntegration implements PluginType
|
|
{
|
|
queue: PQueue;
|
|
|
|
constructor()
|
|
{
|
|
this.queue = new PQueue({ concurrency: 8, interval: 1000, intervalCap: 4, strict: true });
|
|
}
|
|
|
|
async apiCall<T> (subPath: string, query: string)
|
|
{
|
|
const access_token = await secrets.get({ service: 'gamflow_twitch', name: 'access_token' });
|
|
const headers = new Headers({
|
|
"Client-ID": process.env.TWITCH_CLIENT_ID ?? '',
|
|
Authorization: `Bearer ${access_token}`,
|
|
Accept: "application/json"
|
|
});
|
|
const response = await this.queue.add(() => fetch(`https://api.igdb.com/v4${subPath}`, {
|
|
headers: headers,
|
|
method: "POST",
|
|
body: query
|
|
}));
|
|
if (response.ok)
|
|
{
|
|
return response.json() as T;
|
|
}
|
|
}
|
|
|
|
async cleanup ()
|
|
{
|
|
this.queue.clear();
|
|
}
|
|
|
|
async load (ctx: PluginLoadingContextType)
|
|
{
|
|
await checkLoginAndRefreshTwitch();
|
|
|
|
ctx.hooks.games.gameLookup.tapPromise(desc.name, async ({ source, id, search, matches }) =>
|
|
{
|
|
if (!process.env.TWITCH_CLIENT_ID) return;
|
|
const access_token = await secrets.get({ service: 'gamflow_twitch', name: 'access_token' });
|
|
if (!access_token)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((source === 'igdb' && id) || search)
|
|
{
|
|
const client = igdb.igdb(process.env.TWITCH_CLIENT_ID, access_token);
|
|
|
|
const { data: games } = await this.queue.add(() => client.request('games')
|
|
.pipe(...(search ? [igdb.search(search)] : []),
|
|
igdb.fields(['id', 'name', 'summary', 'screenshots.image_id', 'slug', 'first_release_date', 'rating', 'genres.name', 'involved_companies.company.name', 'keywords.name', 'game_modes.name', 'cover.image_id', 'age_ratings.rating_category.rating', 'platforms.name', 'platforms.abbreviation', 'platforms.slug']),
|
|
...(source === 'igdb' && id ? [igdb.where('id', '=', Number(id))] : []),
|
|
igdb.limit(10)).execute());
|
|
|
|
matches.push(...games.filter(g => !!g.name)
|
|
.map(g =>
|
|
{
|
|
const lookup: GameLookup = {
|
|
source: 'igdb',
|
|
id: String(g.id),
|
|
coverUrl: g.cover ? `https://images.igdb.com/igdb/image/upload/t_720p/${g.cover.image_id}.webp` : undefined,
|
|
screenshotUrls: g.screenshots?.map(s => `https://images.igdb.com/igdb/image/upload/t_720p/${s.image_id}.webp`) ?? [],
|
|
name: g.name!,
|
|
summary: g.summary,
|
|
genres: g.genres?.map(g => g.name!) ?? [],
|
|
companies: g.involved_companies?.filter(c => c.company?.name).map(c => c.company?.name!) ?? [],
|
|
game_modes: g.game_modes?.map(m => m.name!) ?? [],
|
|
age_ratings: g.age_ratings?.map(r => r.rating_category?.rating!) ?? [],
|
|
player_count: undefined,
|
|
// UNIX date, needs to be converted
|
|
first_release_date: g.first_release_date ? g.first_release_date * 1000 : undefined,
|
|
average_rating: g.rating ?? undefined,
|
|
keywords: g.keywords?.map(k => k.name!) ?? [],
|
|
igdb_id: g.id ?? undefined,
|
|
platforms: g.platforms?.map(p => ({ id: p.id!, name: p.abbreviation, displayName: p.name!, slug: p.slug! })) ?? [],
|
|
slug: g.slug
|
|
};
|
|
|
|
return lookup;
|
|
}));
|
|
|
|
return;
|
|
}
|
|
});
|
|
|
|
ctx.hooks.games.platformLookup.tapPromise(desc.name, async ({ source, id, slug }) =>
|
|
{
|
|
let query: string | undefined = undefined;
|
|
if (source && id)
|
|
{
|
|
if (source !== 'igdb') return;
|
|
query = `fields name, slug, platform_logo.image_id, platform_logo.url, platform_family.name; where id = ${id};`;
|
|
|
|
}
|
|
else if (slug)
|
|
{
|
|
query = `fields name, slug, platform_logo.image_id, platform_logo.url, platform_family.name; where slug = "${slug}";`;
|
|
}
|
|
|
|
if (query)
|
|
{
|
|
const data = await this.apiCall<[any]>('/platforms', query);
|
|
if (!data || data.length <= 0) return;
|
|
return {
|
|
slug: data[0].slug,
|
|
url_logo: `https://images.igdb.com/igdb/image/upload/t_logo_med/${data[0].platform_logo.image_id}.png`,
|
|
name: data[0].name,
|
|
family_name: data[0].platform_family?.name
|
|
};
|
|
}
|
|
});
|
|
}
|
|
} |