Made utils for interacting with the CurseForge API

This commit is contained in:
Kir_Antipov 2021-09-23 16:22:46 +03:00
parent e38f6957c3
commit 27004e5242
3 changed files with 126 additions and 0 deletions

21
package-lock.json generated
View file

@ -2778,6 +2778,22 @@
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
} }
}, },
"formdata-node": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.2.2.tgz",
"integrity": "sha512-rDQeb6tk/Noep0MXvKhctr5x3gSpeJ+e5gYLFGM4jAt0MilYQLDR1jq61u60Piig3/EtxB/xBZ8WLTmunE5BoA==",
"requires": {
"node-domexception": "1.0.0",
"web-streams-polyfill": "4.0.0-beta.1"
},
"dependencies": {
"web-streams-polyfill": {
"version": "4.0.0-beta.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz",
"integrity": "sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ=="
}
}
},
"fs.realpath": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3923,6 +3939,11 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true "dev": true
}, },
"node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
},
"node-fetch": { "node-fetch": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0.tgz",

View file

@ -41,6 +41,7 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.5.0", "@actions/core": "^1.5.0",
"fast-glob": "^3.2.7", "fast-glob": "^3.2.7",
"formdata-node": "^4.2.2",
"node-fetch": "^3.0.0" "node-fetch": "^3.0.0"
} }
} }

View file

@ -0,0 +1,104 @@
import fetch from "node-fetch";
import { FormData } from "formdata-node";
import { fileFromPath } from "formdata-node/file-from-path";
import { File } from "../utils/file-utils";
import { findVersionByName } from "./minecraft-utils";
const baseUrl = "https://minecraft.curseforge.com/api";
interface CurseForgeVersion {
id: number;
gameVersionTypeID: number;
name: string;
slug: string;
}
interface CurseForgeVersions {
gameVersions: CurseForgeVersion[];
loaders: CurseForgeVersion[];
java: CurseForgeVersion[];
}
let cachedCurseForgeVersions: CurseForgeVersions = null;
async function getCurseForgeVersions(token: string): Promise<CurseForgeVersions> {
if (!cachedCurseForgeVersions) {
cachedCurseForgeVersions = await loadCurseForgeVersions(token);
}
return cachedCurseForgeVersions;
}
async function loadCurseForgeVersions(token: string): Promise<CurseForgeVersions> {
const versionTypes = <{ id: number, slug: string }[]>await (await fetch(`${baseUrl}/game/version-types?token=${token}`)).json();
const javaVersionTypes = versionTypes.filter(x => x.slug.startsWith("java")).map(x => x.id);
const minecraftVersionTypes = versionTypes.filter(x => x.slug.startsWith("minecraft")).map(x => x.id);
const loaderVersionTypes = versionTypes.filter(x => x.slug.startsWith("modloader")).map(x => x.id);
const versions = <CurseForgeVersion[]>await (await fetch(`${baseUrl}/game/versions?token=${token}`)).json();
return versions.reduce((container, version) => {
if (javaVersionTypes.includes(version.gameVersionTypeID)) {
container.java.push(version);
} else if (minecraftVersionTypes.includes(version.gameVersionTypeID)) {
container.gameVersions.push(version);
} else if (loaderVersionTypes.includes(version.gameVersionTypeID)) {
container.loaders.push(version);
}
return container;
}, { gameVersions: new Array<CurseForgeVersion>(), loaders: new Array<CurseForgeVersion>(), java: new Array<CurseForgeVersion>() });
}
export async function unifyGameVersion(gameVersion: string): Promise<string> {
gameVersion = gameVersion.trim();
const minecraftVersion = await findVersionByName(gameVersion);
if (minecraftVersion) {
return `${minecraftVersion.name}${(minecraftVersion.isSnapshot ? "-Snapshot" : "")}`;
}
return gameVersion.replace(/([^\w]|_)+/g, ".").replace(/[.-][a-zA-Z]\w+$/, "-Snapshot");
}
export function unifyJava(java: string): string {
java = java.trim();
const match = java.match(/(?:\d+\D)?(\d+)$/);
if (match && match.length === 2) {
return `Java ${match[1]}`;
}
return java;
}
async function addVersionIntersectionToSet(curseForgeVersions: CurseForgeVersion[], versions: string[], unify: (v: string) => string | Promise<string>, comparer: (cfv: CurseForgeVersion, v: string) => boolean, intersection: Set<number> ) {
for (const version of versions) {
const unifiedVersion = await unify(version);
const curseForgeVersion = curseForgeVersions.find(x => comparer(x, unifiedVersion));
if (curseForgeVersion) {
intersection.add(curseForgeVersion.id);
}
}
}
export async function convertToCurseForgeVersions(gameVersions: string[], loaders: string[], java: string[], token: string): Promise<number[]> {
const versions = new Set<number>();
const curseForgeVersions = await getCurseForgeVersions(token);
await addVersionIntersectionToSet(curseForgeVersions.gameVersions, gameVersions, unifyGameVersion, (cfv, v) => cfv.name === v, versions);
await addVersionIntersectionToSet(curseForgeVersions.loaders, loaders, x => x.trim().toLowerCase(), (cfv, v) => cfv.slug === v, versions);
await addVersionIntersectionToSet(curseForgeVersions.java, java, unifyJava, (cfv, v) => cfv.name === v, versions);
return [...versions];
}
export async function uploadFile(id: string, data: Record<string, any>, file: File, token: string): Promise<number> {
const form = new FormData();
form.append("file", await fileFromPath(file.path), file.name);
form.append("metadata", JSON.stringify(data));
const response = await fetch(`${baseUrl}/projects/${id}/upload-file?token=${token}`, {
method: "POST",
body: <any>form
});
if (!response.ok) {
throw new Error(`Failed to upload file: ${response.status} (${response.statusText})`)
}
return (<{ id: number }>await response.json()).id;
}