diff --git a/package.json b/package.json index 9f240fe..bfa9270 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "devDependencies": { "@types/color": "^3.0.6", "@types/express": "^4.17.21", + "@types/morgan": "^1.9.9", "@types/node": "^20.14.2", "@types/seedrandom": "^3.0.8", "typescript": "^5.4.5" @@ -25,6 +26,7 @@ "color": "^4.2.3", "express": "^4.19.2", "isolated-vm": "^5.0.0", + "morgan": "^1.10.0", "seedrandom": "^3.0.5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd398c2..9e4fdaa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: isolated-vm: specifier: ^5.0.0 version: 5.0.0 + morgan: + specifier: ^1.10.0 + version: 1.10.0 seedrandom: specifier: ^3.0.5 version: 3.0.5 @@ -31,6 +34,9 @@ devDependencies: '@types/express': specifier: ^4.17.21 version: 4.17.21 + '@types/morgan': + specifier: ^1.9.9 + version: 1.9.9 '@types/node': specifier: ^20.14.2 version: 20.14.2 @@ -116,6 +122,12 @@ packages: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true + /@types/morgan@1.9.9: + resolution: {integrity: sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==} + dependencies: + '@types/node': 20.14.2 + dev: true + /@types/node@20.14.2: resolution: {integrity: sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==} dependencies: @@ -214,6 +226,13 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: false + /basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + dependencies: + safe-buffer: 5.1.2 + dev: false + /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -818,6 +837,19 @@ packages: hasBin: true dev: false + /morgan@1.10.0: + resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==} + engines: {node: '>= 0.8.0'} + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.0.2 + transitivePeerDependencies: + - supports-color + dev: false + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: false @@ -889,6 +921,13 @@ packages: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} dev: false + /on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + /on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -896,6 +935,11 @@ packages: ee-first: 1.1.1 dev: false + /on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -1003,6 +1047,10 @@ packages: glob: 7.2.3 dev: false + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: false + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: false diff --git a/src/index.ts b/src/index.ts index 62c03e5..adfd7e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,9 +5,12 @@ import { generatePicrew } from "./picrew.js"; import { dirname, isValidSha256 } from "./util.js"; import { HttpRespondError } from "./types.js"; import path from "path"; +import morgan from "morgan"; const app = express(); +app.use(morgan("tiny")); + app.get("/generate/:makerid/:hash", async (req: express.Request, res: express.Response) => { try { const makerid = req.params.makerid as string; @@ -19,12 +22,16 @@ app.get("/generate/:makerid/:hash", async (req: express.Request, res: express.Re if (["true", "1"].includes(`${req.query.gravatar || ""}`.toLowerCase())) { const gravatarExists = !!(await axios.head(`https://gravatar.com/avatar/${hash}?d=404`).catch(() => false)); - if (gravatarExists) return res.redirect(`https://gravatar.com/avatar/${hash}?s=256`); + if (gravatarExists) { + console.log(`Redirecting to Gravatar: ${hash}`); + return res.redirect(`https://gravatar.com/avatar/${hash}?s=256`); + } } const path = await generatePicrew(hash, makerid); res.sendFile(path); } catch(e) { + console.log(e); if (e instanceof HttpRespondError) { res.status(e.statusCode).send(e.message); } else { diff --git a/src/picrew.ts b/src/picrew.ts index a4f2f53..9d60091 100644 --- a/src/picrew.ts +++ b/src/picrew.ts @@ -1,7 +1,6 @@ import { createCanvas, loadImage } from "canvas"; import { fetchScript, extractDataFromScript, getAsset, dirname } from "./util.js"; import fs from "fs/promises"; -import crypto from "crypto"; import path from "path"; import Color from "color"; import seedrandom from "seedrandom"; @@ -9,11 +8,14 @@ import seedrandom from "seedrandom"; // Generates a picrew from a given maker ID using the provided hash. // Returns the output file path. export async function generatePicrew(hash: string, maker: string) { + console.log(`Request for ${hash}:${maker}`); + const outDir = path.join(dirname, "..", "cache", "outputs", maker); const filePath = path.join(outDir, hash + ".png"); // If the file is already cached, just send that again if (!!(await fs.stat(filePath).catch(() => false))) { + console.log(`${hash}:${maker} already exists, responding with cached file`); return filePath; } @@ -85,9 +87,9 @@ export async function generatePicrew(hash: string, maker: string) { if (canHide) { // If there's other things that would be blocked by this, - // it should be more likely for this one to be hidden so - // the other item appears more frequently. - if (rng() < (inRuleset ? 0.5 : 0.2)) continue; + // it should possible for this one to be hidden so + // the other item can appear too. + if (rng() < (inRuleset ? 0.5 : 0)) continue; } // Check if a conflicting object already exists, and if so, skip this one @@ -134,5 +136,6 @@ export async function generatePicrew(hash: string, maker: string) { await fs.writeFile(filePath, canvas.toBuffer()); + console.log(`Generated ${hash}:${maker}, responding with generated file`); return filePath; } diff --git a/src/util.ts b/src/util.ts index 4960625..1ec0984 100644 --- a/src/util.ts +++ b/src/util.ts @@ -36,6 +36,7 @@ export async function fetchScript(makerId: string) { try { await downloadToPath(`https://picrew.me/en/image_maker/${encodeURIComponent(makerId)}`, htmlPath); } catch(e) { + await fs.unlink(htmlPath).catch(() => {}); if (e instanceof AxiosError && e.status == 404) { throw new HttpRespondError(`There is no Picrew maker with ID ${makerId}.`, 400); } diff --git a/web/index.html b/web/index.html index fd1dc6d..7eeefe0 100644 --- a/web/index.html +++ b/web/index.html @@ -33,7 +33,7 @@ -

Picvatar

+

Picvatar

Generate unique avatars from a Picrew - Like Gravatar, but cooler!

@@ -91,6 +91,18 @@

+

The boring stuff

+

+ If you plan on using this, please respect the copyright and license of the Picrew - it's not my job to enforce other people's copyright, so please be reasonable. I also recommend self-hosting this - a Dockerfile and example docker-compose config is provided in the repository. +

+

+ This is a one-day side project of mine that I'll likely never touch again, but the source code is available here.
+ Reverse engineering Picrew and trying to figure out how to use the available data to manually render images was a fun challenge, but it is nothing I intend to put long term maintenance into. If you find a bug, feel free to report it, but if Picrew completely changes their backend then I will likely not rewrite everything from scratch to accomodate that. +

+

+ Enjoy <3 +

+