From b794ca333c8eb99470297106942e7d6bf6bdb240 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Thu, 5 Jun 2025 18:00:42 -0400 Subject: [PATCH] Typescript fixes --- packages/skin-database/cli.ts | 17 +- packages/skin-database/data/SkinModel.ts | 3 +- packages/skin-database/package.json | 1 + packages/skin-database/tasks/DeleteSkin.ts | 8 - packages/skin-database/tasks/detectRepacks.ts | 168 ------------------ .../skin-database/tasks/tweetMilestones.ts | 4 +- 6 files changed, 10 insertions(+), 191 deletions(-) delete mode 100644 packages/skin-database/tasks/DeleteSkin.ts delete mode 100644 packages/skin-database/tasks/detectRepacks.ts diff --git a/packages/skin-database/cli.ts b/packages/skin-database/cli.ts index dd32d9ac..87b05ada 100755 --- a/packages/skin-database/cli.ts +++ b/packages/skin-database/cli.ts @@ -35,7 +35,6 @@ import { setHashesForSkin } from "./skinHash"; import * as S3 from "./s3"; import { generateDescription } from "./services/openAi"; import KeyValue from "./data/KeyValue"; -import detectRepacks from "./tasks/detectRepacks"; async function withHandler( cb: (handler: DiscordEventHandler) => Promise @@ -389,10 +388,6 @@ program "Compute the order in which skins should be displayed in the museum" ) .option("--foo", "Learn about missing skins") - .option( - "--winampskinsinfo", - "Detect skins that were broken by winampskins.info injecting an ad file" - ) .option("--test-cloudflare", "Try to upload to cloudflare") .action(async (arg) => { const { @@ -404,16 +399,12 @@ program configureR2Cors, computeMuseumOrder, foo, - winampskinsinfo, testCloudflare, } = arg; if (testCloudflare) { const buffer = new Buffer("testing", "utf8"); await S3.putTemp("hello", buffer); } - if (winampskinsinfo) { - await detectRepacks(); - } if (computeMuseumOrder) { await Skins.computeMuseumOrder(); console.log("Museum order updated."); @@ -460,10 +451,12 @@ program } if (foo) { const ctx = new UserContext(); - const missingModernSkins = await KeyValue.get("missingModernSkins"); + const missingModernSkins = await KeyValue.get( + "missingModernSkins" + ); const missingModernSkinsSet = new Set(missingModernSkins); const skins = {}; - for (const md5 of missingModernSkins) { + for (const md5 of missingModernSkins!) { const skin = await SkinModel.fromMd5(ctx, md5); if (skin == null) { continue; @@ -489,7 +482,7 @@ program .select(); console.log(`Found ${skinRows.length} skins to update`); const missingModernSkins = new Set( - await KeyValue.get("missingModernSkins") + await KeyValue.get("missingModernSkins") ); const skins = skinRows.map((row) => new SkinModel(ctx, row)); for (const skin of skins) { diff --git a/packages/skin-database/data/SkinModel.ts b/packages/skin-database/data/SkinModel.ts index c728da0a..a6e0fe7e 100644 --- a/packages/skin-database/data/SkinModel.ts +++ b/packages/skin-database/data/SkinModel.ts @@ -249,7 +249,7 @@ export default class SkinModel { const response = await fetch(this.getSkinUrl()); if (!response.ok) { const missingModernSkins = - (await KeyValue.get("missingModernSkins")) ?? []; + (await KeyValue.get("missingModernSkins")) ?? []; const missingModernSkinsSet = new Set(missingModernSkins); missingModernSkinsSet.add(this.getMd5()); await KeyValue.set( @@ -410,6 +410,7 @@ export default class SkinModel { getZip = mem(async (): Promise => { const buffer = await this.getBuffer(); + // @ts-ignore TODO: Is this typing correct? return JSZip.loadAsync(buffer); }); diff --git a/packages/skin-database/package.json b/packages/skin-database/package.json index 3be80459..648f9186 100644 --- a/packages/skin-database/package.json +++ b/packages/skin-database/package.json @@ -55,6 +55,7 @@ "sync": "ts-node --transpile-only ./tasks/syncWithArchive.ts", "migrate": "knex migrate:latest", "grats": "grats", + "typecheck": "tsc --noEmit", "dev:next": "next dev", "build:next": "next build", "start:next": "next start", diff --git a/packages/skin-database/tasks/DeleteSkin.ts b/packages/skin-database/tasks/DeleteSkin.ts deleted file mode 100644 index c612693c..00000000 --- a/packages/skin-database/tasks/DeleteSkin.ts +++ /dev/null @@ -1,8 +0,0 @@ -import Task from "./ITask"; -import * as Skins from "../data/skins"; - -export default class DeleteSkin extends Task { - async run(): Promise { - await Skins.deleteSkin(md5); - } -} diff --git a/packages/skin-database/tasks/detectRepacks.ts b/packages/skin-database/tasks/detectRepacks.ts deleted file mode 100644 index e5eba16f..00000000 --- a/packages/skin-database/tasks/detectRepacks.ts +++ /dev/null @@ -1,168 +0,0 @@ -import KeyValue from "../data/KeyValue"; -import SkinModel from "../data/SkinModel"; -import UserContext from "../data/UserContext"; -import { knex } from "../db"; - -export default async function detectRepacks() { - const ctx = new UserContext(); - const stored = await KeyValue.get<{ [key: string]: string[] }>( - "winampskins.info" - ); - if (stored == null) { - throw new Error("Expected kv"); - } - - // console.log(stored); - - let found = 0; - let corrupt = 0; - let identical = 0; - - for (const [key, orignalCandidates] of Object.entries(stored)) { - const repack = await SkinModel.fromMd5Assert(ctx, key); - - for (const originalHash of orignalCandidates) { - const original = await SkinModel.fromMd5Assert(ctx, originalHash); - if (await isRepackOf(repack, original)) { - console.log(`${key} is a repack of ${originalHash}`); - found++; - } else if (await areIdentical(repack, original)) { - console.log(`${key} is an identical skin to ${originalHash}`); - identical++; - } else if (await isCorruptRepack(repack, original)) { - console.log(`${key} is a CORRUPT repack of ${originalHash}`); - corrupt++; - } - } - } - - console.log( - `Found ${found} originals out of ${Object.keys(stored).length} repacks` - ); - console.log( - `Found ${identical} twins out of ${Object.keys(stored).length} repacks` - ); - console.log( - `Found ${corrupt} corrupt repacks out of ${ - Object.keys(stored).length - } repacks` - ); -} - -// A skin is a repack if it contains exactly the same files except for the addition of advertizing files -async function isRepackOf( - repack: SkinModel, - original: SkinModel -): Promise { - const nonAdHashesSet = await getSkinArchiveFileHashesSansAds(repack); - const hashesSet = await getSkinArchiveFileHashes(original); - return setsAreEqual(nonAdHashesSet, hashesSet); -} - -async function isCorruptRepack( - repack: SkinModel, - original: SkinModel -): Promise { - const repackHashesSet = await getSkinArchiveFileHashes(repack); - const hashesSet = await getSkinArchiveFileHashes(original); - return setContains(repackHashesSet, hashesSet); -} - -async function areIdentical( - repack: SkinModel, - original: SkinModel -): Promise { - const repackHashesSet = await getSkinArchiveFileHashes(repack); - const hashesSet = await getSkinArchiveFileHashes(original); - return setsAreEqual(repackHashesSet, hashesSet); -} - -async function getSkinArchiveFileHashes(skin: SkinModel): Promise> { - const archiveFiles = await skin.getArchiveFiles(); - const nonAdHashes = archiveFiles.map((f) => f.getFileMd5()); - - return new Set(nonAdHashes); -} - -async function getSkinArchiveFileHashesSansAds( - skin: SkinModel -): Promise> { - const archiveFiles = await skin.getArchiveFiles(); - const nonAdFiles = archiveFiles.filter( - (f) => !f.getFileName().match(/winampskins\.info/) - ); - - const nonAdHashes = nonAdFiles.map((f) => f.getFileMd5()); - - return new Set(nonAdHashes); -} - -// Get all the skins that have ad files in them -async function getRepackSkins(ctx: UserContext) { - const rows = await knex.raw( - `SELECT DISTINCT(skin_md5) FROM archive_files WHERE file_name = "winampskins.info.txt";` - ); - return Promise.all( - rows.map((row) => { - return SkinModel.fromMd5Assert(ctx, row.skin_md5); - }) - ); -} - -function setsAreEqual(a: Set, b: Set): boolean { - if (a === b) return true; - if (a.size !== b.size) { - return false; - } - for (const value of a) if (!b.has(value)) return false; - return true; -} - -// Does a contain b? -function setContains(a: Set, b: Set): boolean { - if (a === b) return true; - if (a.size < b.size) { - return false; - } - for (const value of b) if (!a.has(value)) return false; - return true; -} - -async function foo() { - const repacks = await getRepackSkins(ctx); - for (const skin of repacks) { - const md5 = skin.getMd5(); - if (stored[md5] != null) { - console.log(`Already computed ${md5}`); - continue; - } - const archiveFiles = await skin.getArchiveFiles(); - const archiveFilesMd5 = archiveFiles.map((af) => af.getFileMd5()); - - const matches = await knex.raw(` - SELECT - skin_md5 - FROM - ( - SELECT - skin_md5, - COUNT(*) as total_files, - SUM(CASE WHEN file_md5 IN (${archiveFilesMd5 - .map((m) => `"${m}"`) - .join(",")}) THEN 1 ELSE 0 END) as matching_files - FROM - archive_files - GROUP BY - skin_md5 - ) AS t - WHERE - total_files = matching_files; - `); - const filtered = matches - .map((r) => r.skin_md5) - .filter((m) => m != skin_md5); - stored[skin_md5] = filtered; - await KeyValue.update("winampskins.info", stored); - console.log(`Stored that ${skin_md5} matches ${JSON.stringify(filtered)}`); - } -} diff --git a/packages/skin-database/tasks/tweetMilestones.ts b/packages/skin-database/tasks/tweetMilestones.ts index 330efbcf..f30e29f1 100644 --- a/packages/skin-database/tasks/tweetMilestones.ts +++ b/packages/skin-database/tasks/tweetMilestones.ts @@ -103,8 +103,8 @@ export async function followerCount(handler: DiscordEventHandler) { const user = response.data; const followerCount = user.followers_count; - const current: { count: number } = await KeyValue.get(FOLLOW_COUNT_KEY); - const currentNumber = current.count; + const current = await KeyValue.get<{ count: number }>(FOLLOW_COUNT_KEY); + const currentNumber = current!.count; const nextMilestone = currentNumber + FOLLOW_COUNT_BRACKET_SIZE;