From 81c5563e3dfc28021a64b9dc98cae20efa72818a Mon Sep 17 00:00:00 2001 From: user Date: Mon, 20 Jul 2026 11:40:29 -0300 Subject: [PATCH] initial: clone themelings --- .env.TEMPLATE | 6 + .github/workflows/update.yml | 75 ++++++ .gitignore | 180 ++++++++++++++ .vscode/extensions.json | 3 + .vscode/settings.json | 30 +++ README.md | 14 ++ biome.json | 41 ++++ bun.lock | 94 ++++++++ bunfig.toml | 2 + local.env.TEMPLATE | 8 + package.json | 33 +++ src/canvas/factory.ts | 97 ++++++++ src/canvas/fonts/ggmono/normal-400.ttf | Bin 0 -> 62284 bytes src/canvas/fonts/ggsans/normal-400.ttf | Bin 0 -> 109672 bytes src/canvas/fonts/ggsans/semibold-600.ttf | Bin 0 -> 110048 bytes src/canvas/index.test.ts | 12 + src/canvas/index.ts | 232 ++++++++++++++++++ src/canvas/mock/FirstMockIcon.png | Bin 0 -> 1612 bytes src/canvas/mock/FourthMockIcon.png | Bin 0 -> 2201 bytes src/canvas/mock/LottieMockIcon.lottie | Bin 0 -> 2944 bytes src/canvas/mock/SecondMockIcon.png | Bin 0 -> 888 bytes src/canvas/mock/SecondMockIconNew.png | Bin 0 -> 3106 bytes src/canvas/mock/SvgMockIcon.svg | 19 ++ src/canvas/mock/ThirdMockIcon.png | Bin 0 -> 687 bytes src/canvas/placeholders/lottie.png | Bin 0 -> 23589 bytes src/canvas/placeholders/watermark.svg | 7 + src/canvas/utils.ts | 45 ++++ src/check.ts | 19 ++ src/mock.ts | 147 ++++++++++++ src/types.ts | 56 +++++ src/update/deminify.test.ts | 252 ++++++++++++++++++++ src/update/deminify.ts | 128 ++++++++++ src/update/git.ts | 51 ++++ src/update/index.ts | 89 +++++++ src/update/progress.ts | 112 +++++++++ src/update/runner.ts | 124 ++++++++++ src/update/shared.ts | 20 ++ src/update/tasks/code.ts | 96 ++++++++ src/update/tasks/colors.test.ts | 14 ++ src/update/tasks/colors.ts | 133 +++++++++++ src/update/tasks/decompile-gzip.ts | 11 + src/update/tasks/decompile.ts | 51 ++++ src/update/tasks/diffs.ts | 290 +++++++++++++++++++++++ src/update/tasks/icons.test.ts | 10 + src/update/tasks/icons.ts | 114 +++++++++ src/update/tasks/webhook.ts | 271 +++++++++++++++++++++ src/update/utils.ts | 74 ++++++ tsconfig.json | 31 +++ 48 files changed, 2991 insertions(+) create mode 100644 .env.TEMPLATE create mode 100644 .github/workflows/update.yml create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 biome.json create mode 100644 bun.lock create mode 100644 bunfig.toml create mode 100644 local.env.TEMPLATE create mode 100644 package.json create mode 100644 src/canvas/factory.ts create mode 100644 src/canvas/fonts/ggmono/normal-400.ttf create mode 100644 src/canvas/fonts/ggsans/normal-400.ttf create mode 100644 src/canvas/fonts/ggsans/semibold-600.ttf create mode 100644 src/canvas/index.test.ts create mode 100644 src/canvas/index.ts create mode 100644 src/canvas/mock/FirstMockIcon.png create mode 100644 src/canvas/mock/FourthMockIcon.png create mode 100644 src/canvas/mock/LottieMockIcon.lottie create mode 100644 src/canvas/mock/SecondMockIcon.png create mode 100644 src/canvas/mock/SecondMockIconNew.png create mode 100644 src/canvas/mock/SvgMockIcon.svg create mode 100644 src/canvas/mock/ThirdMockIcon.png create mode 100644 src/canvas/placeholders/lottie.png create mode 100644 src/canvas/placeholders/watermark.svg create mode 100644 src/canvas/utils.ts create mode 100644 src/check.ts create mode 100644 src/mock.ts create mode 100644 src/types.ts create mode 100644 src/update/deminify.test.ts create mode 100644 src/update/deminify.ts create mode 100644 src/update/git.ts create mode 100644 src/update/index.ts create mode 100644 src/update/progress.ts create mode 100644 src/update/runner.ts create mode 100644 src/update/shared.ts create mode 100644 src/update/tasks/code.ts create mode 100644 src/update/tasks/colors.test.ts create mode 100644 src/update/tasks/colors.ts create mode 100644 src/update/tasks/decompile-gzip.ts create mode 100644 src/update/tasks/decompile.ts create mode 100644 src/update/tasks/diffs.ts create mode 100644 src/update/tasks/icons.test.ts create mode 100644 src/update/tasks/icons.ts create mode 100644 src/update/tasks/webhook.ts create mode 100644 src/update/utils.ts create mode 100644 tsconfig.json diff --git a/.env.TEMPLATE b/.env.TEMPLATE new file mode 100644 index 00000000000..e07d0e013c5 --- /dev/null +++ b/.env.TEMPLATE @@ -0,0 +1,6 @@ +# optiopnal, used for crossposting messages +bot_token="Bot ................." +# these are required +code_webhook="https://discord.com/api/webhooks/.../......" +color_webhook="https://discord.com/api/webhooks/.../......" +icons_webhook="https://discord.com/api/webhooks/.../......" \ No newline at end of file diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml new file mode 100644 index 00000000000..4f09a66789b --- /dev/null +++ b/.github/workflows/update.yml @@ -0,0 +1,75 @@ +name: update (hourly) + +on: + workflow_dispatch: + inputs: + force: + description: Force update regardless of "bun run check" + default: false + required: false + type: boolean + quiet: + description: Disable sending any messages + default: false + required: false + type: boolean + schedule: + - cron: "30 * * * *" + +jobs: + check-update: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: main + path: main + - uses: actions/checkout@v4 + with: + ref: data + path: data + + - uses: actions/setup-python@v6 + with: + python-version: "3.14.1" + + - uses: oven-sh/setup-bun@v2 + + - working-directory: main + run: bun install --frozen-lockfile + + - name: Setup Git + working-directory: data + run: | + git config user.name github-actions + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + + - name: Check for version + id: check + working-directory: main + run: | + version=$(bun run check ${{ github.event.inputs.force == 'true' && '--force' || '' }}) + echo "[DEBUG]: Check result: $version" + echo "version=$version" >> $GITHUB_OUTPUT + + - name: Update + if: ${{ steps.check.outputs.version != 'false' }} + env: + code_webhook: ${{ secrets.CODE_WEBHOOK }} + color_webhook: ${{ secrets.COLOR_WEBHOOK }} + icons_webhook: ${{ secrets.ICONS_WEBHOOK }} + bot_token: ${{ secrets.BOT_TOKEN }} + working-directory: main + run: bun run update:prod ${{ github.event.inputs.quiet == 'true' && '--quiet' || '' }} + + - name: Publish changes + if: ${{ steps.check.outputs.version != 'false' }} + working-directory: data + run: git push + + - name: Tag changes + if: ${{ steps.check.outputs.version != 'false' }} + working-directory: data + run: | + git tag -f ${{ steps.check.outputs.version }} + git push origin -f ${{ steps.check.outputs.version }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..4675ad4ba1a --- /dev/null +++ b/.gitignore @@ -0,0 +1,180 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store + +# Custom +tmp +*.env +temp \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000000..16e8e6664e4 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["biomejs.biome"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..eaf2d121048 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,30 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll.biome": "explicit", + "source.organizeImports.biome": "explicit" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[markdown]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[toml]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[yaml]": { + "editor.defaultFormatter": "biomejs.biome" + } +} diff --git a/README.md b/README.md new file mode 100644 index 00000000000..4723b520e1b --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +
+ + Discord server + +

🎨 Themelings

+
+ +A repo that tracks the [Discord](https://github.com/discord) Android app's code, color and icon changes! + +For a closer look at what's scraped, take a look at the [data](https://github.com/nexpid/Themelings/tree/data) branch + +## Credits + +- [hermes-dec](https://github.com/P1sec/hermes-dec) — tool for decompiling hermes React Native bytecode \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 00000000000..e63874eb234 --- /dev/null +++ b/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.11/schema.json", + "formatter": { + "enabled": true, + "indentStyle": "tab", + "lineWidth": 120 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off" + }, + "complexity": { + "noUselessStringConcat": "error", + "noForEach": "error", + "noUselessUndefined": "error", + "useSimplifiedLogicExpression": "error" + }, + "performance": { + "noDelete": "error", + "noNamespaceImport": "error", + "useTopLevelRegex": "warn" + }, + "style": { + "noUselessElse": "error" + } + } + }, + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + } + }, + "files": { + "includes": ["**/*.*", "!tmp", "!temp", "!node_modules"] + } +} diff --git a/bun.lock b/bun.lock new file mode 100644 index 00000000000..c90719572ee --- /dev/null +++ b/bun.lock @@ -0,0 +1,94 @@ +{ + "lockfileVersion": 1, + "configVersion": 0, + "workspaces": { + "": { + "name": "themelings", + "dependencies": { + "@napi-rs/canvas": "^0.1.97", + "color": "^5.0.3", + "discord-api-types": "^0.38.46", + }, + "devDependencies": { + "@biomejs/biome": "^2.4.11", + "@types/bun": "^1.3.12", + "@types/color": "^4.2.1", + }, + "peerDependencies": { + "typescript": "^6.0.2", + }, + }, + }, + "trustedDependencies": [ + "@biomejs/biome", + ], + "packages": { + "@biomejs/biome": ["@biomejs/biome@2.4.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.11", "@biomejs/cli-darwin-x64": "2.4.11", "@biomejs/cli-linux-arm64": "2.4.11", "@biomejs/cli-linux-arm64-musl": "2.4.11", "@biomejs/cli-linux-x64": "2.4.11", "@biomejs/cli-linux-x64-musl": "2.4.11", "@biomejs/cli-win32-arm64": "2.4.11", "@biomejs/cli-win32-x64": "2.4.11" }, "bin": { "biome": "bin/biome" } }, "sha512-nWxHX8tf3Opb/qRgZpBbsTOqOodkbrkJ7S+JxJAruxOReaDPPmPuLBAGQ8vigyUgo0QBB+oQltNEAvalLcjggA=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wOt+ed+L2dgZanWyL6i29qlXMc088N11optzpo10peayObBaAshbTcxKUchzEMp9QSY8rh5h6VfAFE3WTS1rqg=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-gZ6zR8XmZlExfi/Pz/PffmdpWOQ8Qhy7oBztgkR8/ylSRyLwfRPSadmiVCV8WQ8PoJ2MWUy2fgID9zmtgUUJmw=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-avdJaEElXrKceK0va9FkJ4P5ci3N01TGkc6ni3P8l3BElqbOz42Wg2IyX3gbh0ZLEd4HVKEIrmuVu/AMuSeFFA=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-+Sbo1OAmlegtdwqFE8iOxFIWLh1B3OEgsuZfBpyyN/kWuqZ8dx9ZEes6zVnDMo+zRHF2wLynRVhoQmV7ohxl2Q=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.11", "", { "os": "linux", "cpu": "x64" }, "sha512-TagWV0iomp5LnEnxWFg4nQO+e52Fow349vaX0Q/PIcX6Zhk4GGBgp3qqZ8PVkpC+cuehRctMf3+6+FgQ8jCEFQ=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.11", "", { "os": "linux", "cpu": "x64" }, "sha512-bexd2IklK7ZgPhrz6jXzpIL6dEAH9MlJU1xGTrypx+FICxrXUp4CqtwfiuoDKse+UlgAlWtzML3jrMqeEAHEhA=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-RJhaTnY8byzxDt4bDVb7AFPHkPcjOPK3xBip4ZRTrN3TEfyhjLRm3r3mqknqydgVTB74XG8l4jMLwEACEeihVg=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.11", "", { "os": "win32", "cpu": "x64" }, "sha512-A8D3JM/00C2KQgUV3oj8Ba15EHEYwebAGCy5Sf9GAjr5Y3+kJIYOiESoqRDeuRZueuMdCsbLZIUqmPhpYXJE9A=="], + + "@napi-rs/canvas": ["@napi-rs/canvas@0.1.97", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.97", "@napi-rs/canvas-darwin-arm64": "0.1.97", "@napi-rs/canvas-darwin-x64": "0.1.97", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.97", "@napi-rs/canvas-linux-arm64-gnu": "0.1.97", "@napi-rs/canvas-linux-arm64-musl": "0.1.97", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.97", "@napi-rs/canvas-linux-x64-gnu": "0.1.97", "@napi-rs/canvas-linux-x64-musl": "0.1.97", "@napi-rs/canvas-win32-arm64-msvc": "0.1.97", "@napi-rs/canvas-win32-x64-msvc": "0.1.97" } }, "sha512-8cFniXvrIEnVwuNSRCW9wirRZbHvrD3JVujdS2P5n5xiJZNZMOZcfOvJ1pb66c7jXMKHHglJEDVJGbm8XWFcXQ=="], + + "@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.97", "", { "os": "android", "cpu": "arm64" }, "sha512-V1c/WVw+NzH8vk7ZK/O8/nyBSCQimU8sfMsB/9qeSvdkGKNU7+mxy/bIF0gTgeBFmHpj30S4E9WHMSrxXGQuVQ=="], + + "@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.97", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ok+SCEF4YejcxuJ9Rm+WWunHHpf2HmiPxfz6z1a/NFQECGXtsY7A4B8XocK1LmT1D7P174MzwPF9Wy3AUAwEPw=="], + + "@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.97", "", { "os": "darwin", "cpu": "x64" }, "sha512-PUP6e6/UGlclUvAQNnuXCcnkpdUou6VYZfQOQxExLp86epOylmiwLkqXIvpFmjoTEDmPmXrI+coL/9EFU1gKPA=="], + + "@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.97", "", { "os": "linux", "cpu": "arm" }, "sha512-XyXH2L/cic8eTNtbrXCcvqHtMX/nEOxN18+7rMrAM2XtLYC/EB5s0wnO1FsLMWmK+04ZSLN9FBGipo7kpIkcOw=="], + + "@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.97", "", { "os": "linux", "cpu": "arm64" }, "sha512-Kuq/M3djq0K8ktgz6nPlK7Ne5d4uWeDxPpyKWOjWDK2RIOhHVtLtyLiJw2fuldw7Vn4mhw05EZXCEr4Q76rs9w=="], + + "@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.97", "", { "os": "linux", "cpu": "arm64" }, "sha512-kKmSkQVnWeqg7qdsiXvYxKhAFuHz3tkBjW/zyQv5YKUPhotpaVhpBGv5LqCngzyuRV85SXoe+OFj+Tv0a0QXkQ=="], + + "@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.97", "", { "os": "linux", "cpu": "none" }, "sha512-Jc7I3A51jnEOIAXeLsN/M/+Z28LUeakcsXs07FLq9prXc0eYOtVwsDEv913Gr+06IRo34gJJVgT0TXvmz+N2VA=="], + + "@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.97", "", { "os": "linux", "cpu": "x64" }, "sha512-iDUBe7AilfuBSRbSa8/IGX38Mf+iCSBqoVKLSQ5XaY2JLOaqz1TVyPFEyIck7wT6mRQhQt5sN6ogfjIDfi74tg=="], + + "@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.97", "", { "os": "linux", "cpu": "x64" }, "sha512-AKLFd/v0Z5fvgqBDqhvqtAdx+fHMJ5t9JcUNKq4FIZ5WH+iegGm8HPdj00NFlCSnm83Fp3Ln8I2f7uq1aIiWaA=="], + + "@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.97", "", { "os": "win32", "cpu": "arm64" }, "sha512-u883Yr6A6fO7Vpsy9YE4FVCIxzzo5sO+7pIUjjoDLjS3vQaNMkVzx5bdIpEL+ob+gU88WDK4VcxYMZ6nmnoX9A=="], + + "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.97", "", { "os": "win32", "cpu": "x64" }, "sha512-sWtD2EE3fV0IzN+iiQUqr/Q1SwqWhs2O1FKItFlxtdDkikpEj5g7DKQpY3x55H/MAOnL8iomnlk3mcEeGiUMoQ=="], + + "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + + "@types/color": ["@types/color@4.2.1", "", { "dependencies": { "@types/color-convert": "2" } }, "sha512-ResWeDLy1vozIMbD6JLRKuNBbIcIlBkjTIxVHHd5Cqtm77T+ahH3BWE/PWv1OhFd1HAwcn8no4ig2uTaRXpYQQ=="], + + "@types/color-convert": ["@types/color-convert@2.0.3", "", { "dependencies": { "@types/color-name": "*" } }, "sha512-2Q6wzrNiuEvYxVQqhh7sXM2mhIhvZR/Paq4FdsQkOMgWsCIkKvSGj8Le1/XalulrmgOzPMqNa0ix+ePY4hTrfg=="], + + "@types/color-name": ["@types/color-name@1.1.4", "", {}, "sha512-hulKeREDdLFesGQjl96+4aoJSHY5b2GRjagzzcqCfIrWhe5vkCqIvrLbqzBaI1q94Vg8DNJZZqTR5ocdWmWclg=="], + + "@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="], + + "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + + "color": ["color@5.0.3", "", { "dependencies": { "color-convert": "^3.1.3", "color-string": "^2.1.3" } }, "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA=="], + + "color-convert": ["color-convert@3.1.3", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg=="], + + "color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="], + + "color-string": ["color-string@2.1.4", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg=="], + + "discord-api-types": ["discord-api-types@0.38.46", "", {}, "sha512-Ae7NcagMG+FPxwuQxGCPEHmLCKMm8YBMPWEuF5J3L+KWrlH4XGR3UoVo4Ne8bwhhHXbpf+DxDqOeW2jBFupXCQ=="], + + "typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="], + + "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + } +} diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 00000000000..0d331ac7a2b --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[console] +depth = 8 \ No newline at end of file diff --git a/local.env.TEMPLATE b/local.env.TEMPLATE new file mode 100644 index 00000000000..630594017ff --- /dev/null +++ b/local.env.TEMPLATE @@ -0,0 +1,8 @@ +# used in "bun run update:test" and "bun run update:mock" +NODE_ENV="test" +# optiopnal, used for crossposting messages +bot_token="Bot ................." +# all values below are optional, they are overrides for the base .env file +code_webhook="https://discord.com/api/webhooks/.../......" +color_webhook="https://discord.com/api/webhooks/.../......" +icons_webhook="https://discord.com/api/webhooks/.../......" \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000000..73301f77001 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "themelings", + "type": "module", + "scripts": { + "check": "bun run src/check.ts", + "update:test": "bun run --env-file local.env src/update/index.ts", + "update:commit": "bun run --env-file local.env src/update/index.ts --commit-anyway", + "update:mock": "bun run --env-file local.env src/update/index.ts --mock", + "update:prod": "bun run src/update/index.ts", + "canvas:test": "bun run --watch src/canvas/index.test.ts", + "deminify:test": "bun run src/update/deminify.test.ts", + "colors:test": "bun run src/update/tasks/colors.test.ts", + "icons:test": "bun run src/update/tasks/icons.test.ts", + "format": "biome check --fix ." + }, + "dependencies": { + "@napi-rs/canvas": "^0.1.97", + "color": "^5.0.3", + "discord-api-types": "^0.38.46" + }, + "devDependencies": { + "@biomejs/biome": "^2.4.11", + "@types/bun": "^1.3.12", + "@types/color": "^4.2.1" + }, + "peerDependencies": { + "typescript": "^6.0.2" + }, + "trustedDependencies": [ + "@biomejs/biome" + ], + "packageManager": "bun@1.3.12" +} diff --git a/src/canvas/factory.ts b/src/canvas/factory.ts new file mode 100644 index 00000000000..112b56b3677 --- /dev/null +++ b/src/canvas/factory.ts @@ -0,0 +1,97 @@ +import { join } from "node:path"; +import { type Image, loadImage } from "@napi-rs/canvas"; +import { type Diff, DiffType } from "../types"; + +export type Item = + | { + type: "color"; + colors: string[][]; + } + | { + type: "image"; + image: Image; + } + | { + type: "blob"; + blob: string; + }; + +export interface Row { + title?: string; + subtitle?: string; + item: Item; +} + +export interface Section { + header: string; + columns: Row[][]; +} + +function placeholderFile(file: string) { + return file.endsWith(".lottie") ? join(import.meta.dir, "placeholders/lottie.png") : file; +} + +const numColumns = 5; +const maxRows = 3; + +async function makeSection(diffs: Map, isFile: boolean, type: DiffType, header: string) { + const filtered = [...diffs.entries()].filter(([, diff]) => diff.type === type); + const section: Section = { + header, + columns: Array.from({ length: numColumns }, () => []), + }; + + let i = 0; + for (const [name, diff] of filtered) { + const columnInd = i++ % numColumns; + + const column = section.columns[columnInd], + nextColumn = section.columns[i % numColumns]; + if (!column) throw new Error(`Couldn't find column of index ${columnInd}`); + + if (!nextColumn || nextColumn.length >= maxRows) { + column.push({ + item: { + type: "blob", + blob: `+${(filtered.length - i + 1).toLocaleString("en-US")} ${header.toLowerCase()}`, + }, + }); + break; + } + + const changed = diff.type === DiffType.Changed; + const item: Item = isFile + ? { type: "image", image: await loadImage(placeholderFile(diff.source)) } + : { type: "color", colors: [changed && diff.oldSource.split(","), diff.source.split(",")].filter((x) => !!x) }; + + column.push( + ...[ + changed && + isFile && { + subtitle: name, + item: { + type: "image", + image: await loadImage(placeholderFile(diff.oldSource)), + } as Item, + }, + { + title: name, + subtitle: diff.type === DiffType.Renamed ? diff.oldName : undefined, + item, + }, + ].filter((x) => !!x), + ); + } + + section.columns = section.columns.filter((x) => x.length); + return section; +} + +export async function makeSections(diffs: Map, isFile = false) { + return [ + await makeSection(diffs, isFile, DiffType.Added, "Added"), + await makeSection(diffs, isFile, DiffType.Changed, "Changed"), + await makeSection(diffs, isFile, DiffType.Renamed, "Renamed"), + await makeSection(diffs, isFile, DiffType.Removed, "Removed"), + ].filter((x) => x.columns[0]); +} diff --git a/src/canvas/fonts/ggmono/normal-400.ttf b/src/canvas/fonts/ggmono/normal-400.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3a2aa0cb03591a98e0bb774130a52e7bbe2c99a2 GIT binary patch literal 62284 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

    !`oEDZ$$Pj$-tN2akL>u?xdR(R4=j~P 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 literal 0 HcmV?d00001 diff --git a/src/canvas/fonts/ggsans/semibold-600.ttf b/src/canvas/fonts/ggsans/semibold-600.ttf new file mode 100644 index 0000000000000000000000000000000000000000..28e88c38c8e4329bdd95ca7afbd27d0f4dccc047 GIT binary patch literal 110048 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 Math.max(0, textWidth, itemWidth))); +} + +function calcColumnHeight(column: Row[]) { + const texts = column + .map((row) => Number(!!row.title) + Number(!!row.subtitle) + Number(row.item.type === "blob")) + .reduce((a, b) => a + b, 0); + const items = texts + column.length - 1; + return texts * textSizes.title + column.length * itemSize + items * layout.card.gapY; +} + +function calcSectionHeight(columns: Row[][]) { + return Math.max(0, ...columns.map((column) => calcColumnHeight(column))); +} + +const layout = { + background: { + padding: 24, + gap: 18, + }, + section: { + gap: 8, + }, + card: { + radius: 24, + padding: 12, + gapX: 16, + gapY: 8, + }, + blob: { + radius: 24 - 12, + }, + color: { + radius: 24, + }, +}; + +const textSizes = { + header: 24, + title: 18, + blob: 24, +}; + +const colors = { + background: "#1c1d23", // BACKGROUND_BASE_LOWEST + section: "#26272f", // CARD_BACKGROUND_DEFAULT + blob: "#6c6f7c1f", // BACKGROUND_MOD_MUTED + textNormal: "#c7c8ce", // TEXT_NORMAL + textMuted: "#818491", // TEXT_MUTED + watermark: "#2f3035", // TEXT_OVERLAY_DARK +}; + +const itemSize = 128; +const blobSize = itemSize + layout.card.gapY + textSizes.title; + +const watermark = await transformSvg(await file(join(import.meta.dir, "placeholders/watermark.svg")).text(), { + color: colors.watermark, + height: layout.background.padding, +}); + +const mctx = createCanvas(1, 1).getContext("2d"); +export function drawSections(sections: Section[]) { + const headerFont = `600 ${textSizes.header}px GG Sans`; + const titleFont = `400 ${textSizes.title}px GG Mono`; + + const sects = sections.map(({ header, columns }) => ({ + headerWidth: measureText(mctx, headerFont, header).width, + header, + columns: columns.map((column) => + column.map(({ title, subtitle, item }) => ({ + title, + subtitle, + textWidth: Math.max( + 0, + title ? measureText(mctx, titleFont, title).width : 0, + subtitle ? measureText(mctx, titleFont, subtitle).width : 0, + ), + item, + itemWidth: + item.type === "blob" + ? blobSize + : item.type === "image" + ? (item.image.width / item.image.height) * itemSize + : itemSize, + })), + ), + })); + const columnWidths = Array.from({ length: Math.max(0, ...sects.map(({ columns }) => columns.length)) }, (_, i) => + Math.max(0, ...sects.map(({ columns }) => (columns[i] ? calcColumnWidth(columns[i]) : 0))), + ); + const sectionWidth = + columnWidths.reduce((a, b) => a + b, 0) + layout.card.gapX * (columnWidths.length - 1) + layout.card.padding * 2; + + const width = Math.max(0, ...sects.map(({ headerWidth }) => headerWidth), sectionWidth); + const height = + sects + .map( + ({ columns }) => + textSizes.header + + layout.section.gap + + layout.card.padding * 2 + + calcSectionHeight(columns) + + layout.background.gap, + ) + .reduce((a, b) => a + b, 0) - layout.background.gap; + + const canvas = createCanvas(layout.background.padding * 2 + width, layout.background.padding * 2 + height); + const ctx = canvas.getContext("2d"); + ctx.textBaseline = "top"; + + ctx.fillStyle = colors.background; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + let x = layout.background.padding, + y = layout.background.padding; + for (const sect of sects) { + ctx.fillStyle = colors.textNormal; + ctx.font = headerFont; + ctx.fillText(sect.header, x, y); + y += textSizes.header + layout.section.gap; + + const sectionHeight = calcSectionHeight(sect.columns); + ctx.fillStyle = colors.section; + ctx.beginPath(); + ctx.roundRect(x, y, sectionWidth, sectionHeight + layout.card.padding * 2, layout.card.radius); + ctx.fill(); + y += layout.card.padding; + x += layout.card.padding; + + let wx = x; + for (let i = 0; i < sect.columns.length; i++) { + let wy = y; + + const column = sect.columns[i]; + const columnWidth = columnWidths[i]; + if (!columnWidth) throw new Error(`Failed to get columnWidth for ${i}, ${columnWidth}`); + + const centerX = wx + columnWidth / 2; + for (const row of column) { + ctx.font = titleFont; + if (row.subtitle) { + ctx.fillStyle = colors.textMuted; + ctx.fillText(row.subtitle, centerX - row.textWidth / 2, wy); + wy += textSizes.title + layout.card.gapY; + } + if (row.title) { + ctx.fillStyle = colors.textNormal; + ctx.fillText(row.title, centerX - row.textWidth / 2, wy); + wy += textSizes.title + layout.card.gapY; + } + + const midX = centerX - row.itemWidth / 2; + if (row.item.type === "blob") { + const blobHeight = sectionHeight - wy + y; + + ctx.fillStyle = colors.blob; + ctx.beginPath(); + ctx.roundRect(wx, wy, columnWidth, blobHeight, layout.blob.radius); + ctx.fill(); + + ctx.font = `600 ${textSizes.blob}px GG Sans`; + ctx.fillStyle = colors.textNormal; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + + const wrapped = wrapText(ctx, row.item.blob, columnWidth); + const height = textSizes.blob / 2; + for (let i = 0; i < wrapped.length; i++) + ctx.fillText( + wrapped[i], + wx + columnWidth / 2, + wy + blobHeight / 2 - height * (wrapped.length - 1) + textSizes.blob * i, + ); + + ctx.textAlign = "left"; + ctx.textBaseline = "top"; + } else if (row.item.type === "image") { + ctx.drawImage(row.item.image, midX, wy, row.itemWidth, itemSize); + wy += itemSize + layout.card.gapY; + } else if (row.item.type === "color") { + for (let i = 0; i < row.item.colors.length; i++) { + const colors = row.item.colors[i]; + for (let j = 0; j < colors.length; j++) { + const color = colors[j]; + ctx.save(); + ctx.beginPath(); + ctx.rect( + Math.floor(midX + (itemSize / colors.length) * j), + Math.floor(wy + (itemSize / row.item.colors.length) * i), + Math.ceil(itemSize / colors.length), + Math.ceil(itemSize / row.item.colors.length), + ); + ctx.clip(); + + ctx.beginPath(); + ctx.fillStyle = color; + ctx.roundRect(midX, wy, itemSize, itemSize, layout.color.radius); + ctx.fill(); + ctx.restore(); + } + } + wy += itemSize + layout.card.gapY; + } + } + wx += columnWidth + layout.card.gapX; + } + y += sectionHeight; + + x -= layout.card.padding; + y += layout.card.padding; + y += layout.background.gap; + } + + // watermark + ctx.drawImage(watermark, canvas.width - watermark.width - 1, 1); + + return canvas; +} diff --git a/src/canvas/mock/FirstMockIcon.png b/src/canvas/mock/FirstMockIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..580037001a03cf1c9892cedfee8553062059ec99 GIT binary patch literal 1612 zcmV-S2DABzP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!Ts3VK~#8N?VDY4 z+cpr#7oeJEGJbGRP`4MT(hJm-E!pZz?OvdEFW@8>u-pqoIf301D0x!!6}l&|a)P)g zh&ydP9*5)>D}fn}B#;0}$W({F7Xh$l3H-HK01FyQN=iyfO4b%8KUYS_^ESV{hwX|3 zUx#5>WsFrtFE#;TOMYbvgKLIxjfgiP!h7KS@~#5oL)X2L-&hGN5r{#_!1a<9XRl&>AKgl*&0>_>JGeZ2k#PJmYrB@Thej(C7xZ|MCZ zn=+HxgD3z$@!?c-@0n~W1RStkukSpU)zoAXXethRqF9}_6pnFNVTW0!Txx+R-jF*l z>|ct82;iZ^+_WO45@>D2i4Y>)I_znhO=hYf&3){wjW~qpD}@3I39>~SV}W`D@1^)C zYRrkFy5WanF)_nvigo*=BhJ{ltSZ^7*OuDt4c>`eKz6d&#{i%2@9(r_H4$wOn#3K> zjDLZm!5nr>*NYQ}P#*qr>b!0HMA9@<3UucupSM(oL%=gx%!F-OU!!>3ExuH~cD~BhGW6l@hgWKO*jYrt~23kndzsiNm;dM-C6&8yal0Q9BBK z_6_ZCVuYU+TSD%!nXYg&QSRbr9h179ZZo| zIwq&%2pR+f|4>$y*tSm~U41K13MACm>y-E&rC-wwqS0uKLxlLfj&1uy%vjX|nLvZ^ z@k4(&I;XRnQEHO~7qX{9D^fSw1SR(G1k9iaRH@3jH8_l;S7_d+hDhBimrVG^;p#c zG0Zmx%|V@?Twhzl$UYC1=RKuBj~ple1P_J_YEwml|SMiSA(35*sk%+teaoJv(DX{WHHn>K1YNFiaICF14~b63GEpER>2_l+LHMxOsC_GIN@Ay21VWH&lDr56 z7`~9jShPHd$lHrd&slkUD!cU+*BeVsgXz%BI zAN3$`c9yhq&7v(3@ne0bjSOGO>e_*b?Y&*s=)T`bAYy-ahc5fApY(GF;cCBO{;vtf z0uhfY`gcx+K*XIeU_Z;65k@P?EZwfR#T{{C*1SJ^Fs9SS_DKq##d=#&OE6-Xch|GA>wfn;*$8qKO*3ZJj5Y# z@t_EbOWB4i6yYFIN=6r@}z+o9??rCRhI?6 zoKx`-8<_yB!bDgVD1xS*^9B4Q;?zM_v8JNnA9w%oHoY_@o2{`DfoA#qs$9so=`A*T zQET!p(tMJgu7eS7#A-C=oR5X4{CWGgbGZspN=iyfN=p6@0Q?7pJN$!Rg$%O*0000< KMNUMnLSTaZ=lTNx literal 0 HcmV?d00001 diff --git a/src/canvas/mock/FourthMockIcon.png b/src/canvas/mock/FourthMockIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..4bb486b21c858a5d0147345dd25d3859725cf34f GIT binary patch literal 2201 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 b|Ht(oULJj!lVNyT00000NkvXXu0mjfJ&G1+ literal 0 HcmV?d00001 diff --git a/src/canvas/mock/LottieMockIcon.lottie b/src/canvas/mock/LottieMockIcon.lottie new file mode 100644 index 0000000000000000000000000000000000000000..a2a4fc1216a11994a0ab1d39014bfa0621d17245 GIT binary patch literal 2944 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 WRC6PW8~;2eyV|HLU%x_I0N@|YO?4#z literal 0 HcmV?d00001 diff --git a/src/canvas/mock/SecondMockIcon.png b/src/canvas/mock/SecondMockIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..0d7eab30520742e3aa2beefb7cdf80fb1e77f127 GIT binary patch literal 888 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2KK0(?STfpq`;`#{vU@Nw^gM|}$) zLQwC*N4<+4^+Kc;KL#rRN<9S%f)zaJ2g-ukKs7)SAlbVR#OMLC7e49+2|fWD2$p@^ z4>q}X;e$S)I8f#M`#oTj!N%SPX#r^mkw6ikcA!Y#;>SQI0hNJW^$2Jk&_Ix05Cduv zP%#97)PoRE@dJ>02)Xd_Le5j7Kp%RQ1o;Is2n6KMKY#!K{`CzG^$Pd==f98d?>m?9 zub|=Gf_)AN`x6`HG45NizCJ+FaROsLBcmYW!UjhFeTD%H%M@`TVd=Nk^@U7gCN~xwzLNXzdP7q0v}=!6UXQ$(P?&Xk%?qE?_XI9Z z5$zS@FQ2>ci#%ohEDOE~WG0#3uJO-}gUo`6qJpW&4k?Cl4&Af7xca#d-JV$F{T1^?!ev zkoPA1p<-rfjmCyX_2zYb{ZA?n8vopwxD@s#e82{tC(?S(~|NXwNCZcH|BUleaFIKFhne92Rx%WIvN8n0e^zWm((Gqpc|y)|?H$8eS{az^wM#e2X6#^CAd=d#Wz Gp$Pzcs^6vn literal 0 HcmV?d00001 diff --git a/src/canvas/mock/SecondMockIconNew.png b/src/canvas/mock/SecondMockIconNew.png new file mode 100644 index 0000000000000000000000000000000000000000..f83d0103c6b73b115f6d4a7e8a9461729a173870 GIT binary patch literal 3106 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 literal 0 HcmV?d00001 diff --git a/src/canvas/mock/SvgMockIcon.svg b/src/canvas/mock/SvgMockIcon.svg new file mode 100644 index 00000000000..b3a9647b356 --- /dev/null +++ b/src/canvas/mock/SvgMockIcon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/canvas/mock/ThirdMockIcon.png b/src/canvas/mock/ThirdMockIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..65718d5454b97489f61d0625ff71f444ed98f5da GIT binary patch literal 687 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~Q-Dv1E0F$A8Q_S&)CM#~v?Rzc znBjiHdxM7g4*LZZ7Sx}Q56JIdKcQfYlRYN`0~5Qai(^Q|t+&@s79Ca)V7b7kxG`h% z+%NzCXQlM4?rxcK;Ku~#UrUz0y2>HY{O5W4G{>9e7K{y5>?zTT$tl(BDQwz#T6-Bb zY_qUXn4-eoqwCjF^zzZ1*-aAq&&p!nmHM?9WwOuX7h&MpcgQn9?Ink$;KvS5Q9ea4 zyK5Vl7BXGaY8P0#Av^b|kG$iX!qCS8UP6!A4}_i*SM=iLo&;2ORY2CsTtvlr#W_jW zrc)|?7q7gSveY21X0kEwv)M;XEFB`V${RxCCcgP|H_%ZdQKFXjv8zgkt9sqc{jDmN z+DfLA!lFA;Q_pcd<$oF2Q@V+^6J2DykX!2I7l)abnJ+rZe<@k_qGi(N1>(Cdy=;E* z#mRWCUWcE$%X02TmpL!KY`ef-*gSt?s)5q2tIl^GOmYl{qZI?pS|t-3_*X9mx$ zP>0NBd4>j|8#&8wm5FrkY!{jVC~vUj4Yg zA`IMOx{FUL3og!aRN6AHPV9Kurzsq&$LBjX?!TheqvrBl zC2DHl%vl0+H{V(Mx7~lmHNj{byVooiE&H7(G>I$}b3e3v%A)rIbNhP2)|FH}dTL*< a8}BEUcFLjW*BxM*VeoYIb6Mw<&;$Sn1x1Sh literal 0 HcmV?d00001 diff --git a/src/canvas/placeholders/lottie.png b/src/canvas/placeholders/lottie.png new file mode 100644 index 0000000000000000000000000000000000000000..1a69318a9b11b58d028d46216a1e0ef38712d9bb GIT binary patch literal 23589 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 + + + + + + \ No newline at end of file diff --git a/src/canvas/utils.ts b/src/canvas/utils.ts new file mode 100644 index 00000000000..73d93bbdde0 --- /dev/null +++ b/src/canvas/utils.ts @@ -0,0 +1,45 @@ +import { type CanvasRenderingContext2D, loadImage } from "@napi-rs/canvas"; + +export function measureText(ctx: CanvasRenderingContext2D, font: string, text: string) { + ctx.font = font; + return ctx.measureText(text); +} + +export function wrapText(ctx: CanvasRenderingContext2D, text: string, maxWidth: number) { + // https://stackoverflow.com/a/16599668 + const words = text.split(" "); + let lines: string[] = [], + line = words[0]; + + for (let i = 1; i < words.length; i++) { + const word = words[i]; + const newText = `${line} ${word}`; + + const width = ctx.measureText(newText).width; + if (width < maxWidth) line = newText; + else { + lines.push(line); + line = word; + } + } + + lines.push(line); + return lines; +} + +export async function transformSvg(svg: string, config: { color?: string; height?: number; width?: number }) { + const text = config.color ? svg.replace(/fill="#[a-z0-9]{3,6}"/gi, `fill="${config.color}"`) : svg; + + const image = await loadImage(Buffer.from(text)); + if (config.width && config.height) { + image.width = config.width; + image.height = config.height; + } else if (config.width) { + image.height = (image.height / image.width) * config.width; + image.width = config.width; + } else if (config.height) { + image.width = (image.width / image.height) * config.height; + image.height = config.height; + } + return image; +} diff --git a/src/check.ts b/src/check.ts new file mode 100644 index 00000000000..b819f30a8d0 --- /dev/null +++ b/src/check.ts @@ -0,0 +1,19 @@ +import { trackerUserAgent } from "./update/shared"; + +const tracker = await fetch("https://tracker.vendetta.rocks/tracker/index", { + headers: { + "User-Agent": trackerUserAgent, + }, +}); +if (!tracker.ok || tracker.status !== 200) throw new Error("Failed to get version from tracker!"); + +const data = (await tracker.json()) as { latest: { alpha: number } }; +const alpha = data.latest.alpha.toString(); + +const file = Bun.file("../data/version.txt"); +const latest = (await file.exists()) && (await file.text()); + +if (latest !== alpha || process.argv.includes("--force")) { + await file.write(alpha); + process.stdout.write(alpha); +} else process.stdout.write("false"); diff --git a/src/mock.ts b/src/mock.ts new file mode 100644 index 00000000000..a7f1a07a7b6 --- /dev/null +++ b/src/mock.ts @@ -0,0 +1,147 @@ +import Color from "color"; +import { type CodeDiff, type Diff, DiffType } from "./types"; + +const randomColor = () => + `#${Math.floor(Math.random() * 0xffffff) + .toString(16) + .padStart(6, "0")}`; +const randomColors = () => { + const base = Color(randomColor()); + return [ + base.lighten(0.6).hex(), + base.desaturate(0.3).darken(0.5).hex(), + base.darken(0.6).hex(), + base.darken(0.8).hex(), + ].join(","); +}; + +const semNum = 20; + +// mock data for "bun run update:mock" and "bun run canvas:test" +export default { + semantic: new Map([ + ...Array.from({ length: semNum }, (_, i) => [ + `MOCK_SEMANTIC_SAMPLE_${i + 1}`, + { + type: DiffType.Added, + source: randomColors(), + label: "☀️ #aaaaaa, 🌙 #bbbbbb", + } as Diff, + ]), + ...Array.from({ length: semNum }, (_, i) => [ + `MOCK_SEMANTIC_SAMPLE_${i + 1 + semNum}`, + { + type: DiffType.Changed, + source: randomColors(), + label: "☀️ #aaaaaa, 🌙 #bbbbbb", + oldSource: randomColors(), + oldLabel: "☀️ #888888, 🌙 #999999", + } as Diff, + ]), + ...Array.from({ length: semNum }, (_, i) => [ + `MOCK_SEMANTIC_SAMPLE_${i + 1 + semNum * 2}`, + { + type: DiffType.Renamed, + oldName: `mocked_samentic_sample_${i + 1 + semNum * 2}`, + source: randomColors(), + } as Diff, + ]), + ...Array.from({ length: semNum }, (_, i) => [ + `MOCK__SEMANTIC__REMOVED__SAMPLE__${i + 1 + semNum * 3}`, + { + type: DiffType.Removed, + source: randomColors(), + label: "☀️ #aaaaaa, 🌙 #bbbbbb", + } as Diff, + ]), + ] as [string, Diff][]), + semanticBig: new Map( + Array.from({ length: 35 }, (_, i) => [ + `SOME_EXAMPLE_SEMANTIC_${i}`, + { + type: DiffType.Changed, + source: randomColors(), + label: "☀️ #aaaaaa, 🌙 #bbbbbb", + oldSource: randomColors(), + oldLabel: "☀️ #888888, 🌙 #999999", + } as Diff, + ]), + ), + raw: new Map(), + icons: new Map([ + [ + "FirstMockIcon", + { + type: DiffType.Added, + source: "src/canvas/mock/FirstMockIcon.png", + label: "c6f6357f", + } as Diff, + ], + [ + "SvgMockIcon", + { + type: DiffType.Added, + source: "src/canvas/mock/SvgMockIcon.svg", + label: "5d534560", + } as Diff, + ], + [ + "LottieMockIcon", + { + type: DiffType.Added, + source: "src/canvas/mock/LottieMockIcon.lottie", + label: "340efc52", + } as Diff, + ], + [ + "SecondMockIcon", + { + type: DiffType.Changed, + source: "src/canvas/mock/SecondMockIconNew.png", + label: "76ffeecd", + oldSource: "src/canvas/mock/SecondMockIcon.png", + oldLabel: "6e235744", + } as Diff, + ], + [ + "ThirdMockIcon", + { + type: DiffType.Renamed, + oldName: "thirdmkocicon", + source: "src/canvas/mock/ThirdMockIcon.png", + } as Diff, + ], + [ + "FourthMockIcon", + { + type: DiffType.Removed, + source: "src/canvas/mock/FourthMockIcon.png", + label: "cf4e1340", + } as Diff, + ], + ]), + code: new Map([ + [ + "app/foomodule.tsx", + { + type: DiffType.Added, + size: 24782, + } as CodeDiff, + ], + [ + "app/ui/barmodule.ts", + { + type: DiffType.Renamed, + oldName: "app/legacy/ui/bar_module.ts", + size: 5212, + } as CodeDiff, + ], + [ + "app/intl/ReallyBigCoolComponent.tsx", + { + type: DiffType.Removed, + size: 46532, + } as CodeDiff, + ], + ]), +}; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000000..2994c6feaf3 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,56 @@ +export enum DiffType { + Added, + Changed, + Renamed, + Removed, +} + +export type Diff = + | { + type: DiffType.Added | DiffType.Removed; + source: string; + label?: string; + } + | { + type: DiffType.Changed; + source: string; + label?: string; + oldSource: string; + oldLabel?: string; + } + | { + type: DiffType.Renamed; + oldName: string; + source: string; + }; + +export type CodeDiff = + | { + type: DiffType.Added | DiffType.Removed; + size: number; + } + | { + type: DiffType.Renamed; + oldName: string; + size: number; + }; + +export type Differs = Record<"semantic" | "raw" | "icons", Map | undefined> & { + code: Map | undefined; +}; + +export type RawColors = Record; + +export type Icons = Record< + string, + { + file: string; + hash: string; + scales: number[]; + width: number | null; + height: number | null; + } +>; + +export type Semantic = Record; +export type SemanticColors = Record; diff --git a/src/update/deminify.test.ts b/src/update/deminify.test.ts new file mode 100644 index 00000000000..e0e691789b3 --- /dev/null +++ b/src/update/deminify.test.ts @@ -0,0 +1,252 @@ +import { deminify } from "./deminify"; + +const SNIPPET = `r7 = function(a0, a1, a2, a3, a4, a5, a6) { // Environment: r3 + r4 = a1; + r2 = a5; + r5 = a6; + var _closure1_slot0 = r4; + r0 = a2; + var _closure1_slot1 = r0; + var _closure1_slot2 = r5; + r0 = global; + r7 = r0.Object; + r6 = r7.defineProperty; + r1 = {}; + r0 = true; + r1['value'] = r0; + r0 = '__esModule'; + r0 = r6.bind(r7)(r2, r0, r1); + r0 = 0; + r6 = r5[r0]; + r1 = a3; + r0 = undefined; + r1 = r1.bind(r0)(r6); + r1 = 1; + r1 = r5[r1]; + r1 = r4.bind(r0)(r1); + r1 = r1.jsx; + var _closure1_slot3 = r1; + r1 = {}; + r6 = function(a0) { // Original name: openLazy, environment: r3 + _fun32591: for(var _fun32591_ip = 0; ; ) switch(_fun32591_ip) { +case 0: + r3 = a0; + r2 = r3.importer; + r4 = r3.hideActionSheet; + r1 = undefined; + if(!(r4 === r1)) { _fun32591_ip = 25; continue _fun32591 } +case 23: + r4 = true; +case 25: + var _closure2_slot0 = r4; + r3 = r3.isDismissable; + if(!(r3 === r1)) { _fun32591_ip = 41; continue _fun32591 } +case 39: + r3 = false; +case 41: + var _closure2_slot1 = r3; + r2 = r2.bind(r1)(); + r1 = r2.then; + r0 = function(a0) { // Environment: r0 + _fun32592: for(var _fun32592_ip = 0; ; ) switch(_fun32592_ip) { +case 0: + r0 = _closure2_slot0; + if(!r0) { _fun32592_ip = 45; continue _fun32592 } +case 10: + r2 = _closure1_slot1; + r1 = _closure1_slot2; + r0 = 2; + r1 = r1[r0]; + r0 = undefined; + r1 = r2.bind(r0)(r1); + r0 = r1.hideActionSheet; + r0 = r0.bind(r1)(); +case 45: + r2 = _closure1_slot1; + r1 = _closure1_slot2; + r0 = 3; + r1 = r1[r0]; + r0 = undefined; + r3 = r2.bind(r0)(r1); + r2 = r3.dispatch; + r1 = {}; + r5 = 'ALERT_OPEN'; + r1['type'] = r5; + r5 = a0; + r1['alert'] = r5; + r4 = _closure2_slot1; + r1['isDismissable'] = r4; + r1 = r2.bind(r3)(r1); + return r0; + } + }; + r0 = r1.bind(r2)(r0); + return r0; + } + }; + r1['openLazy'] = r6; + r6 = function() { // Original name: close, environment: r3 + r2 = _closure1_slot1; + r1 = _closure1_slot2; + r0 = 3; + r1 = r1[r0]; + r0 = undefined; + r3 = r2.bind(r0)(r1); + r2 = r3.dispatch; + r1 = {}; + r4 = 'ALERT_CLOSE'; + r1['type'] = r4; + r1 = r2.bind(r3)(r1); + return r0; + }; + r1['close'] = r6; + r6 = function(a0) { // Original name: show, environment: r3 + _fun32594: for(var _fun32594_ip = 0; ; ) switch(_fun32594_ip) { +case 0: + r7 = a0; + r3 = this; + r5 = r7.hideActionSheet; + r4 = r7.isDismissable; + r1 = r7.confirmText; + r0 = undefined; + if(!(r1 === r0)) { _fun32594_ip = 90; continue _fun32594 } +case 32: + r10 = _closure1_slot0; + r11 = _closure1_slot2; + r2 = 4; + r8 = r11[r2]; + r8 = r10.bind(r0)(r8); + r9 = r8.intl; + r8 = r9.string; + r2 = r11[r2]; + r2 = r10.bind(r0)(r2); + r2 = r2.t; + r2 = r2.BddRzc; + r1 = r8.bind(r9)(r2); +case 90: + var _closure2_slot0 = r1; + r2 = {'hideActionSheet': 0, 'isDismissable': 0, 'confirmText': 0}; + r13 = null; + r14 = r2; + r1 = silentSetPrototypeOf(r14, r13); + r14 = {}; + r13 = r7; + r12 = r2; + r1 = copyDataProperties(r14, r13, r12); + var _closure2_slot1 = r1; + r2 = r3.openLazy; + r1 = {}; + r6 = function() { // Original name: importer, environment: r6 + r2 = _closure1_slot0; + r0 = _closure1_slot2; + r1 = 6; + r1 = r0[r1]; + r3 = undefined; + r2 = r2.bind(r3)(r1); + r1 = 5; + r1 = r0[r1]; + r0 = r0.paths; + r2 = r2.bind(r3)(r1, r0); + r1 = r2.then; + r0 = function(a0) { // Environment: r0 + r1 = a0; + r1 = r1.default; + var _closure4_slot0 = r1; + r0 = function(a0) { // Environment: r0 + r3 = _closure1_slot3; + r2 = _closure4_slot0; + r1 = {}; + r5 = a0; + r6 = r1; + r0 = copyDataProperties(r6, r5); + r5 = _closure2_slot1; + r6 = r1; + r4 = copyDataProperties(r6, r5); + r4 = _closure2_slot0; + r0 = 'confirmText'; + r1[r0] = r4; + r0 = undefined; + r0 = r3.bind(r0)(r2, r1); + return r0; + }; + return r0; + }; + r0 = r1.bind(r2)(r0); + return r0; + }; + r1['importer'] = r6; + r1['hideActionSheet'] = r5; + r1['isDismissable'] = r4; + r1 = r2.bind(r3)(r1); + return r0; + } + }; + r1['show'] = r6; + r3 = function(a0) { // Original name: confirm, environment: r3 + r1 = this; + var _closure2_slot0 = r1; + r1 = a0; + var _closure2_slot1 = r1; + r1 = global; + r2 = r1.Promise; + r1 = r2.prototype; + r1 = Object.create(r1, {constructor: {value: r2}}); + r3 = function(a0) { // Environment: r0 + r0 = a0; + var _closure3_slot0 = r0; + r3 = _closure2_slot0; + r2 = r3.show; + r1 = {}; + r0 = function() { // Original name: onConfirm, environment: r5 + r2 = _closure3_slot0; + r0 = undefined; + r1 = true; + r1 = r2.bind(r0)(r1); + return r0; + }; + r1['onConfirm'] = r0; + r9 = _closure1_slot0; + r10 = _closure1_slot2; + r6 = 4; + r7 = r10[r6]; + r0 = undefined; + r7 = r9.bind(r0)(r7); + r8 = r7.intl; + r7 = r8.string; + r6 = r10[r6]; + r6 = r9.bind(r0)(r6); + r6 = r6.t; + r6 = r6.ETE/oK; + r6 = r7.bind(r8)(r6); + r1['cancelText'] = r6; + r5 = function() { // Original name: onCancel, environment: r5 + r2 = _closure3_slot0; + r0 = undefined; + r1 = false; + r1 = r2.bind(r0)(r1); + return r0; + }; + r1['onCancel'] = r5; + r11 = _closure2_slot1; + r12 = r1; + r4 = copyDataProperties(r12, r11); + r1 = r2.bind(r3)(r1); + return r0; + }; + r4 = r1; + r0 = new r4[r2](r3, r2); + r0 = r0 instanceof Object ? r0 : r1; + return r0; + }; + r1['confirm'] = r3; + r3 = 7; + r3 = r5[r3]; + r5 = r4.bind(r0)(r3); + r4 = r5.fileFinishedImporting; + r3 = 'actions/native/AlertActionCreators.tsx'; + r3 = r4.bind(r5)(r3); + r2['default'] = r1; + return r0; +};`; + +await Bun.write("temp/deminified.js", deminify(SNIPPET, "actions/native/AlertActionCreators.js").res); diff --git a/src/update/deminify.ts b/src/update/deminify.ts new file mode 100644 index 00000000000..4379439ad7b --- /dev/null +++ b/src/update/deminify.ts @@ -0,0 +1,128 @@ +import { writeFileSync } from "node:fs"; +import { extname } from "node:path"; +import { log } from "./progress"; + +const variableMatch = /\br(\d{1,2})\b/g; +const argumentMatch = /\ba(\d{1,2})\b/g; + +const funMatch = /\b(_fun)(\d+)(_ip)?(?: = (\d+))?\b/g; +const caseMatch = /^case (\d+):/g; + +const functNameMatch = /(function\*?) ?(\(.*?\) {) \/\/ Original name: +\??(.+), environment: \w+$/g; +const functOpenMatch = /function\*? ?\((.*?)\) {/; +const functCloseMatch = /^\s+};/; + +const stringMatch = /(["'`])(?:\\.|[^\\])*?\1/g; +// biome-ignore lint/suspicious/noControlCharactersInRegex: this is needed +const nullstringMatch = /(["'`])\x00(\d+)\x00\1/g; + +const nativeModuleNames = [ + "global", + "require", + "metroImportDefault", + "metroImportAll", + "moduleObject", + "exports", + "dependencyMap", +]; + +export function deminify(code: string, path: string) { + const cleaned = code + .split(" = ") + .slice(1) + .join(" = ") + .slice(0, -1) + .replace(/ \/\/ Environment: \w+$/gm, "") + .split("\n"); + + const lines: string[] = []; + + let depth = 0; + const localArgs = new Map(); + const funMap = new Map(); + const caseMap = new Map(); + caseMap.set("0", 0); + + const varReplacer = (_: string, id: string) => `var${Number(id) + 1}`; + const argReplacer = (_: string, id: string) => { + const n = Number(id), + depth = localArgs.get(n); + if (!depth) log(`WARNING! arg${n} does not have any depth (${path})`); + + if (depth === 1) return nativeModuleNames[n] ?? `native${n + 1}`; + return `arg${n + 1}`; + }; + const funReplacer = (_: string, prefix: string, id: string, suffix: string, caseId?: string) => { + const num = funMap.get(id) ?? String(funMap.size + 1).padStart(4, "0"); + funMap.set(id, num); + + const text = `${prefix}${num}${suffix ?? ""}`; + if (!caseId) return text; + + const caseNum = caseMap.get(caseId) ?? caseMap.size + 1; + caseMap.set(caseId, caseNum); + return `${text} = ${caseNum}`; + }; + const caseReplacer = (_: string, id: string) => { + const num = caseMap.get(id) ?? caseMap.size + 1; + caseMap.set(id, num); + return `case ${num}:`; + }; + const functNameReplacer = (_: string, prefix: string, stuff: string, name: string) => `${prefix} ${name}${stuff}`; + + let j = 1; + for (const raw of cleaned) { + const nullStrings = new Map(); + const line = raw.replace(stringMatch, (match, quot: string) => { + const index = nullStrings.size + 1; + nullStrings.set(String(index), match); + return `${quot}\x00${index}\x00${quot}`; + }); + + // check for start of function + if (functOpenMatch.test(line)) { + const openedArgs = line.match(functOpenMatch)?.[1]?.split(", ")?.length || 0; + + depth++; + for (let i = 0; i < openedArgs; i++) { + localArgs.set(i, depth); + } + } + + // check for end of function + if (functCloseMatch.test(line)) { + for (const arg of localArgs.keys()) { + const n = localArgs.get(arg); + if (n === depth) localArgs.set(arg, n - 1); + } + depth--; + } + + // sanity check + const y = j++; + if (depth < 0) { + writeFileSync("temp/depthcrashed.js", cleaned.join("\n")); + throw new Error(`parsing failed, depth went below 0 (LINE: ${y}, FILE: ${path})`); + } + + const parsed = line + .replace(variableMatch, varReplacer) + .replace(argumentMatch, argReplacer) + .replace(funMatch, funReplacer) + .replace(caseMatch, caseReplacer) + .replace(functNameMatch, functNameReplacer) + .replace(nullstringMatch, (_, __, id: string) => { + const nullString = nullStrings.get(id); + if (nullString) return nullString; + writeFileSync("temp/nullcrashed.js", cleaned.join("\n")); + throw new Error(`parsing failed, nullstring ${id} doesn't exist (LINE: ${y}, FILE: ${path})`); + }); + lines.push(parsed); + } + + const final = lines.join("\n"); + return { + final, + res: `// ${path}\n${extname(path).startsWith(".ts") ? "export default" : "module.exports ="} (${final})();`, + }; +} diff --git a/src/update/git.ts b/src/update/git.ts new file mode 100644 index 00000000000..bb592e2ba6f --- /dev/null +++ b/src/update/git.ts @@ -0,0 +1,51 @@ +import { commitAnyway } from "./shared"; +import { handleShellErr } from "./utils"; + +export const gitChanged = new Set(); + +export async function fetchGitChanged() { + for (const changed of ( + await Bun.$`git status -z -- ':!oldicons' ':!icons' ':!source'`.cwd("../data").quiet().then(handleShellErr) + ) + .text() + .split("\x00") + .filter((x) => x !== "") + .map((x) => x.slice(3))) + gitChanged.add(changed); +} + +const gitQueue: Promise[] = []; + +export async function commit(files: string[], message: string) { + while (gitQueue[0]) { + await gitQueue[0]; + } + + const { promise, resolve } = Promise.withResolvers(); + gitQueue.push(promise); + + if (process.env.NODE_ENV === "test" && !commitAnyway) { + await fetchGitChanged(); + } else { + // unstage all files + await Bun.$`git restore --staged .`.cwd("../data").nothrow().quiet().then(handleShellErr); + await fetchGitChanged(); + // stage files + await Bun.$`git add ${{ raw: files.map((x) => Bun.$.escape(x)).join(" ") }}` + .cwd("../data") + .nothrow() + .quiet() + .then(handleShellErr); + // commiiiiiit + await Bun.$`git commit -m ${message}` + .cwd("../data") + .nothrow() + .quiet() + .then((e) => void e); + } + + resolve(); + + const promInd = gitQueue.indexOf(promise); + if (promInd !== -1) gitQueue.splice(promInd, 1); +} diff --git a/src/update/index.ts b/src/update/index.ts new file mode 100644 index 00000000000..1394f41ea0f --- /dev/null +++ b/src/update/index.ts @@ -0,0 +1,89 @@ +import { exists, mkdir, rm } from "node:fs/promises"; +import { log, makeProgress, wrapPromise } from "./progress"; +import { runTasks } from "./runner"; +import { type apkSplits, apksFolder, isMock, trackerUserAgent, version, workFolder } from "./shared"; +import { handleShellErr, join } from "./utils"; + +const cdnUrl = `https://tracker.vendetta.rocks/tracker/download/${version}/`; + +const mediaFiles = Bun.$.braces("res/**/*.{png,jpg,lottie}"); +const apkAssets = { + base: ["assets/index.android.bundle", ...mediaFiles], + "config.hdpi": mediaFiles, + "config.xxhdpi": mediaFiles, +} satisfies Record<(typeof apkSplits)[number], string[]>; + +let reuseFolder = isMock; +if (!reuseFolder) { + const matches = await Bun.file("tmp/ver") + .text() + .then((x) => x === version) + .catch(() => false); + if (matches) { + const exist = await Promise.allSettled( + Object.keys(apkAssets).map((folder) => exists(join(apksFolder, folder, "res"))), + ); + if (exist.every((x) => x.status === "fulfilled" && x.value)) reuseFolder = true; + } +} + +// Temp folder yippee! + +if (!reuseFolder) await rm(workFolder, { force: true, recursive: true }); +if (!(await exists(apksFolder))) await mkdir(apksFolder, { recursive: true }); + +if (!reuseFolder) { + // Download the APKs + log("Downloading APKs..."); + + const downloadProgress = makeProgress( + Object.fromEntries(Object.keys(apkAssets).map((apk) => [apk, `Downloading ${apk}.apk`])), + ); + await Promise.allSettled( + Object.keys(apkAssets).map((apk) => { + return wrapPromise( + fetch(cdnUrl + apk, { + headers: { + "User-Agent": trackerUserAgent, + "Cache-Control": "public, max-age=3600", + }, + }) + .then((res) => res.arrayBuffer()) + .then((data) => Bun.write(join(workFolder, `${apk}.zip`), data)), + downloadProgress, + apk, + ); + }), + ); + if (downloadProgress.anyFailed()) throw new Error(`Failed to download all APKs!\n${downloadProgress.prettyErrors()}`); + + log("\nUnzipping APKs..."); + + const unzipProgress = makeProgress( + Object.fromEntries(Object.keys(apkAssets).map((apk) => [apk, `Unzipping ${apk}.apk`])), + ); + + await Promise.allSettled( + Object.entries(apkAssets).map(([apk, assets]) => + wrapPromise( + Bun.$`unzip -o ${join(workFolder, `${apk}.zip`)} ${{ + raw: assets.map((file) => Bun.$.escape(file)).join(" "), + }} -d ${join(apksFolder, apk)} 2>/dev/null` + .quiet() + .nothrow() + .then(handleShellErr), + unzipProgress, + apk, + ), + ), + ); + if (unzipProgress.anyFailed()) throw new Error(`Failed to unzip all APKs!\n${unzipProgress.prettyErrors()}`); + + await Bun.write(join(workFolder, "ver"), version); +} else log("Reusing folder!"); + +// Run tasks + +await runTasks(); + +log("✅ Done"); diff --git a/src/update/progress.ts b/src/update/progress.ts new file mode 100644 index 00000000000..96d8c025598 --- /dev/null +++ b/src/update/progress.ts @@ -0,0 +1,112 @@ +import { formatError } from "./utils"; + +let somethingInLog = false; +export function log(...messages: any) { + console.log(...messages); + somethingInLog = true; +} + +export function makeProgress(data: Record, startPaused = false) { + type Key = keyof typeof data; + const keys = Object.keys(data); + + const result = new Map(); + const errors = new Map(); + if (startPaused) for (const key of keys) result.set(key, "paused"); + + let logs: string[] = []; + const reprint = () => { + const oldLogs = logs; + logs = keys.map((key) => { + const rootKey = key.split("_")[1] && keys.includes(key.split("_")[0]) && key.split("_")[0]; + const otherKeys = keys.filter((x) => x.split("_")[0] === rootKey); + const isLast = otherKeys.indexOf(key) === otherKeys.length - 1; + + return `${rootKey ? (isLast ? " ╚═ " : " ╠═ ") : ""}${ + { + success: "✅", + failed: "❌", + skipped: "💫", + paused: "🔔", + idle: "⏰", + }[result.get(key) || "idle"] + } ${data[key]}`; + }); + + if (!oldLogs.length) console.log(logs.join("\n")); + else if ( + oldLogs.length === logs.length && + process.stdout.moveCursor && + process.stdout.clearLine && + !somethingInLog + ) { + for (let i = 0; i < logs.length; i++) + if (logs[i] !== oldLogs[i]) { + const j = logs.length - i; + process.stdout.moveCursor(-500, -j); + process.stdout.clearLine(1); + process.stdout.write(logs[i]); + process.stdout.moveCursor(-500, j); + } + } else { + somethingInLog = false; + console.log(`\n${logs.join("\n")}`); + } + }; + + somethingInLog = false; + reprint(); + + const bKeyer = (keys: Key[]) => { + const bKeys: Key[] = []; + for (const k of keys) bKeys.push(k, ...keys.filter((x) => x.startsWith(`${k}_`))); + return bKeys; + }; + + return { + update(key: Key, value: boolean | null, error?: any) { + if (value === true) result.set(key, "success"); + else if (value === false) result.set(key, "failed"); + else if (value === null) result.set(key, "skipped"); + + if (error) errors.set(key, error); + reprint(); + }, + start(key: Key) { + result.set(key, "idle"); + reprint(); + }, + pause(key: Key) { + result.set(key, "paused"); + reprint(); + }, + + anyFailed: () => [...result.values()].some((x) => x === "failed"), + someFailed: (...keys: Key[]) => bKeyer(keys).some((k) => result.get(k) === "failed"), + isFinished: (key: Key) => ["success", "failed", "skipped"].some((x) => result.get(key) === x), + + keys, + errors, + prettyErrors(...keys: Key[]) { + const bKeys = bKeyer(keys); + let err = [...errors.entries()]; + + if (keys[0]) err = err.filter(([k]) => bKeys.includes(k)); + return err.map(([k, v]) => `${k} => ${v}`).join("\n"); + }, + }; +} + +export type Progress = ReturnType; + +export async function wrapPromise(promise: Promise, progress: Progress, key: string): Promise { + progress.start(key); + try { + const value = await promise; + progress.update(key, true); + return value; + } catch (e: any) { + progress.update(key, false, formatError(e)); + throw e; + } +} diff --git a/src/update/runner.ts b/src/update/runner.ts new file mode 100644 index 00000000000..9c010b3af58 --- /dev/null +++ b/src/update/runner.ts @@ -0,0 +1,124 @@ +import { exists, rm } from "node:fs/promises"; +import mock from "../mock"; +import type { Differs } from "../types"; +import { commit } from "./git"; +import { log, makeProgress, wrapPromise } from "./progress"; +import { apksFolder, codePath, cuteVersion, isMock, isQuiet, oprevFiles, prevFiles, version } from "./shared"; +import codeTask from "./tasks/code"; +import colors from "./tasks/colors"; +import decompile from "./tasks/decompile"; +import diffs from "./tasks/diffs"; +import icons from "./tasks/icons"; +import { webhook } from "./tasks/webhook"; +import { formatError, handleShellErr, join } from "./utils"; + +export async function runTasks() { + log("\nRunning tasks..."); + const progress = makeProgress( + { + preinit: "Preinit", + preinit_discard: "Discarding changes", + preinit_save: "Caching original files", + decompile: "Decompilation", + decompile_downloading: "Downloading decompiler", + decompile_decompiling: "Decompiling index.android.bundle", + decompile_gzip: "Compressing code", + code: "Code parser", + code_getting: "Finding file imports", + code_remaking: "Recreating code", + code_pushing: "Committing source", + colors: "Parsing colors", + icons: "Icon parser", + icons_getting: "Writing icons.json", + icons_copying: "Copying images", + diff: "Diffs", + diff_raw: "Diffing raw colors", + diff_semantic: "Diffing semantic colors", + diff_icons: "Diffing icons", + diff_code: "Diffing code", + webhook: "Sending webhook messages", + }, + true, + ); + let differs: Differs | undefined; + + if (!isMock) { + // discard changes + try { + progress.start("preinit"); + + // reset to remote branch (without pulling latest commit) + progress.start("preinit_discard"); + await Bun.$`git reset --hard`.cwd("../data").nothrow().quiet().then(handleShellErr); + if (await exists(join("../data", "oldicons"))) + await rm(join("../data", "oldicons"), { force: true, recursive: true }); + await Bun.write("../data/version.txt", version); // lel + + // unstage all files + await Bun.$`git restore --staged .`.cwd("../data").nothrow().quiet().then(handleShellErr); + + progress.update("preinit_discard", true); + + progress.start("preinit_save"); + for (const oprev of oprevFiles) { + const file = Bun.file(join("../data", oprev)); + if (await file.exists()) prevFiles.set(oprev, await file.arrayBuffer()); + } + progress.update("preinit_save", true); + progress.update("preinit", true); + } catch (e) { + progress.update("preinit", false); + throw new Error(`Failed to discard changes!\n${e}`); + } + + try { + await wrapPromise( + decompile(progress, join(apksFolder, "base", "assets", "index.android.bundle")), + progress, + "decompile", + ); + } catch (e) { + throw new Error(`Failed to decompile!\n${e}`); + } + + const code = (await Bun.file(codePath).text()).replace(/\r/g, "").split("\n"); + + await Promise.allSettled([ + wrapPromise(codeTask(progress, code), progress, "code"), + wrapPromise(colors(code), progress, "colors"), + wrapPromise(icons(progress, code), progress, "icons"), + ]); + if (progress.someFailed("code", "colors", "icons")) + throw new Error(`Failed at parser tasks!\n${progress.prettyErrors("code", "colors", "icons")}`); + + while (!progress.isFinished("decompile_gzip")) { + await Bun.sleep(1000); + } + + if (progress.someFailed("decompile_gzip")) + throw new Error(`Failed at the decompile gzip task!\n${progress.prettyErrors("decompile_gzip")}`); + + try { + differs = await diffs(progress); + progress.update("diff", true); + } catch (e: any) { + progress.update("diff", false, formatError(e)); + throw new Error(`Failed to generate diffs!\n${formatError(e)}`); + } + } else { + differs = mock; + } + + if (differs && !isQuiet) { + try { + await wrapPromise(webhook(differs), progress, "webhook"); + } catch (e: any) { + throw new Error(`Failed to send webhook messages!\n${formatError(e)}`); + } + } else { + progress.update("webhook", null); + } + + await commit(["version.txt"], `chore: bump app version to ${cuteVersion}`); + await rm("../data/oldicons", { force: true, recursive: true }); +} diff --git a/src/update/shared.ts b/src/update/shared.ts new file mode 100644 index 00000000000..3d09c45e6ef --- /dev/null +++ b/src/update/shared.ts @@ -0,0 +1,20 @@ +import { join } from "node:path"; + +export const trackerUserAgent = "Themelings "; +export const maxDiffChanges = 20; + +export const workFolder = "tmp"; +export const apksFolder = join(workFolder, "apks"); +export const codePath = join(workFolder, "code.js"); +export const apkSplits = ["base", "config.hdpi", "config.xxhdpi"] as const; + +export const oprevFiles = ["semantic.json", "raw.json", "icons.json", "source.jsonl"] as const; +export const prevFiles = new Map<(typeof oprevFiles)[number], ArrayBuffer>(); + +export const version = await Bun.file("../data/version.txt").text(); +export const cuteVersion = `${["stable", "beta", "alpha"][Number(version[3])] ?? "version"} ${version.slice(0, 3)}.${Number(version.slice(-2))}`; + +export const isMock = process.argv.includes("--mock"); +export const diffAnyway = process.argv.includes("--diff-anyway"); +export const commitAnyway = process.argv.includes("--commit-anyway"); +export const isQuiet = process.argv.includes("--quiet"); diff --git a/src/update/tasks/code.ts b/src/update/tasks/code.ts new file mode 100644 index 00000000000..533d0b53b2b --- /dev/null +++ b/src/update/tasks/code.ts @@ -0,0 +1,96 @@ +import { mkdir, rm } from "node:fs/promises"; +import { deminify } from "../deminify"; +import { commit } from "../git"; +import type { Progress } from "../progress"; +import { commitAnyway, cuteVersion } from "../shared"; +import { discordPath, join, listRequiredDirs, sortEntries } from "../utils"; + +// scary code matching below, be warned + +const moduleStartIndentation = " ".repeat(4); +const pathCheckRegex = / = '(.*?)';$/; +const lineImportRegex = / = r\d{1,2}\.fileFinishedImporting;$/; + +export default async function code(progress: Progress, code: string[]) { + progress.start("code_getting"); + const files = new Map(); + + let moduleStart: number | null = null; + + for (let i = 0; i < code.length; i++) { + const line = code[i]; + + // find the start of the file import based on the indentation + if ( + new RegExp(`^${moduleStartIndentation}r\\d{1,2} = function\\(`).test(line) && + line.includes(" { // Environment:") + ) { + moduleStart = i; + } + + // easiest way to check + const path = code[i + 1]?.match(pathCheckRegex)?.[1]; + if (line.match(lineImportRegex) && path) { + const start = moduleStart; + if (!start) throw `moduleStart was null for ${start}~${i}; null ~ ${code[i]}`; + moduleStart = null; + + // find the end of the file import based on the indentation + const moduleEndThing = `${moduleStartIndentation}};`; + let moduleEnd: number | null = null; + for (let j = i; j < code.length; j++) { + if (code[j] === moduleEndThing) { + moduleEnd = i = j; + break; + } + } + + if (!moduleEnd) throw `moduleEnd was null for ${start}~${i}; ${code[start]} ~ ${code[i]}`; + + const realPath = discordPath(path); + files.set( + realPath, + deminify( + code + .slice(start, moduleEnd + 1) + .map((line) => line.replace(new RegExp(`^${moduleStartIndentation}`), "")) + .join("\n"), + realPath, + ), + ); + } + } + + await Bun.write( + "../data/source.jsonl", + sortEntries([...files.entries()]) + .map(([file, text]) => `{ "file": ${JSON.stringify(file)}, "size": ${text.final.length} }`) + .join("\n"), + ); + + progress.update("code_getting", true); + + if (process.env.NODE_ENV !== "test" && !commitAnyway) { + progress.start("code_remaking"); + + const filePrefix = "../data/source"; + await rm(filePrefix, { recursive: true, force: true }); + + const dirs = listRequiredDirs([...files.keys()]); + + await Promise.all(dirs.map((dir) => mkdir(join(filePrefix, dir), { recursive: true }))); + await Promise.all(files.entries().map(([file, text]) => Bun.write(join(filePrefix, file), text.res))); + + progress.update("code_remaking", true); + progress.start("code_pushing"); + + // hope the files get written in this time idk :P blehhh + await Bun.sleep(1500); + + await commit(["source.jsonl", "source/*"], `chore: update source for ${cuteVersion}`); + progress.update("code_pushing", true); + } else { + progress.update("code_remaking", null); + progress.update("code_pushing", null); + } +} diff --git a/src/update/tasks/colors.test.ts b/src/update/tasks/colors.test.ts new file mode 100644 index 00000000000..2046d656a75 --- /dev/null +++ b/src/update/tasks/colors.test.ts @@ -0,0 +1,14 @@ +import { sortObj } from "../utils"; +import { convertSimpleSemantic, getInternalRawColors, getInternalSemanticColors } from "./colors"; + +const codeFile = Bun.file("tmp/code.js"); +if (!(await codeFile.exists())) throw "Missing decompiled code.js"; + +const code = (await codeFile.text()).replace(/\r/g, "").split("\n"); + +const raw = getInternalRawColors(code); +await Bun.write("temp/raw.json", JSON.stringify(sortObj(raw), undefined, 4)); + +const semantic = getInternalSemanticColors(code, raw); +await Bun.write("temp/semantic.json", JSON.stringify(sortObj(semantic), undefined, 4)); +await Bun.write("temp/semantic_simple.json", JSON.stringify(sortObj(convertSimpleSemantic(semantic)), null, 4)); diff --git a/src/update/tasks/colors.ts b/src/update/tasks/colors.ts new file mode 100644 index 00000000000..310b3f11760 --- /dev/null +++ b/src/update/tasks/colors.ts @@ -0,0 +1,133 @@ +import { runInNewContext } from "node:vm"; +import Color, { type ColorInstance } from "color"; +import type { RawColors, SemanticColors } from "../../types"; +import { commit } from "../git"; +import { cuteVersion } from "../shared"; +import { sortObj } from "../utils"; + +function hex(color: ColorInstance) { + return (color.alpha() === 1 ? color.hex() : color.hexa()).toLowerCase(); +} + +// FIXME fixes an issue introduced in hermes-dec commit 1c112f85e3fb49442c57a0ed31b83bb3825db639, +// where code such as "rX[rY] = z" is instead parsed as "rX[Y] = z" +function fixHermesDecIndex(code: string) { + return code.replace(/r(\d+)\[(\d+)\] =/g, "r$1[r$2] ="); +} + +// more scary code matching below, be warned + +export function evalMetroModule(code: string[], importName: string, imports: object = {}): T { + const hookLine = code.findIndex((x) => x.includes(importName)); + if (hookLine === -1) throw new Error(`Cannot find hookLine for ${importName}!`); + + // usually fileFinishedImporting is called 5 lines before the function ends, so we pray + const endLine = hookLine + 5; + + let startLine: number | null = null; + for (let i = hookLine; i > 0; i--) + if (code[i].includes("// Environment:")) { + startLine = i; + break; + } + + if (startLine === null) throw new Error(`Failed to find startLine for ${importName}!`); + + const snippet = fixHermesDecIndex(code.slice(startLine, endLine).join("\n")); + const fn = runInNewContext(snippet, { + global: { + Object: Object.assign(Object, { + freeze(obj: any) { + return obj; + }, + }), + }, + }); + + const exports: any = {}; + fn( + null, // global + () => ({ + fileFinishedImporting() {}, + ...imports, + }), // require + null, // metroImportDefault + null, // metroImportAll + null, // moduleObject + exports, // exports + [], // dependencyMap + ); + + return exports; +} + +export function getInternalRawColors(code: string[]) { + const module = evalMetroModule<{ + _private: { + RawColors: RawColors; + }; + }>(code, "/tokens/colors/generated/raw-color-definitions.tsx'", {}); + + const raw: RawColors = {}; + for (const [key, color] of Object.entries(module._private.RawColors)) { + raw[key] = hex(Color(color)); + } + + return raw; +} + +export function getInternalSemanticColors(code: string[], raw: Record) { + const module = evalMetroModule<{ + _private: { + SemanticColors: Record>; + }; + }>(code, "/tokens/colors/generated/native/generated-definitions.tsx'", { + _private: { + Themes: new Proxy( + {}, + { + get(_, key) { + return key.toString().toLowerCase(); + }, + }, + ), + }, + }); + + const semantic: SemanticColors = {}; + for (const [key, colors] of Object.entries(module._private.SemanticColors)) { + const color: SemanticColors[string] = {}; + for (const [theme, val] of Object.entries(colors)) { + if (typeof val !== "object" || !("raw" in val)) continue; + + color[theme] = [hex(Color(raw[val.raw]).alpha(val.opacity)), { raw: val.raw, opacity: val.opacity }]; + } + + semantic[key] = color; + } + + return semantic; +} + +export function convertSimpleSemantic(semantic: SemanticColors) { + const simpleSemantic: Record> = {}; + for (const [key, colors] of Object.entries(semantic)) { + const color: Record = {}; + for (const [theme, [clr]] of Object.entries(colors)) color[theme] = clr; + + simpleSemantic[key] = color; + } + + return simpleSemantic; +} + +export default async function colors(code: string[]) { + const raw = getInternalRawColors(code); + await Bun.write("../data/raw.json", JSON.stringify(sortObj(raw), null, 4)); + + const semantic = getInternalSemanticColors(code, raw); + await Bun.write("../data/semantic.json", JSON.stringify(sortObj(semantic), null, 4)); + await Bun.write("../data/semantic_simple.json", JSON.stringify(sortObj(convertSimpleSemantic(semantic)), null, 4)); + + await commit(["raw.json", "semantic.json", "semantic_simple.json"], `chore: update colors for ${cuteVersion}`); +} diff --git a/src/update/tasks/decompile-gzip.ts b/src/update/tasks/decompile-gzip.ts new file mode 100644 index 00000000000..438369a0402 --- /dev/null +++ b/src/update/tasks/decompile-gzip.ts @@ -0,0 +1,11 @@ +// prevents TS errors +declare let self: Worker; + +self.addEventListener("message", async ({ data: { path, target } }) => { + await Bun.write( + target, + Bun.gzipSync(await Bun.file(path).arrayBuffer(), { level: 9 }), // slowest speed, best compression + ); + + self.postMessage(true); +}); diff --git a/src/update/tasks/decompile.ts b/src/update/tasks/decompile.ts new file mode 100644 index 00000000000..ebfd17429f3 --- /dev/null +++ b/src/update/tasks/decompile.ts @@ -0,0 +1,51 @@ +import { exists } from "node:fs/promises"; +import { commit } from "../git"; +import type { Progress } from "../progress"; +import { codePath, commitAnyway, cuteVersion, workFolder } from "../shared"; +import { handleShellErr, join } from "../utils"; + +const gzipWorkerURL = new URL("decompile-gzip.ts", import.meta.url).href; +const decompilerPy = "src/hermes_dec/decompilation/hbc_decompiler.py"; + +export default async function decompile(progress: Progress, pathToBundle: string) { + const pathToDecompiler = join(workFolder, "decompiler"); + + progress.start("decompile_downloading"); + if (!(await exists(pathToDecompiler))) { + await Bun.$`git clone https://github.com/P1sec/hermes-dec.git --depth=1 ${pathToDecompiler}` + .quiet() + .nothrow() + .then(handleShellErr); + progress.update("decompile_downloading", true); + } else progress.update("decompile_downloading", null); + + const { exitCode: hasPython } = await Bun.$`python --version`.nothrow().quiet(); + if (hasPython !== 0) throw new Error("Cannot use Python! Are you sure it's installed?"); + + progress.start("decompile_decompiling"); + if (!(await Bun.file(codePath).exists())) { + await Bun.$`python ${join(pathToDecompiler, decompilerPy)} ${pathToBundle} ${codePath}` + .quiet() + .nothrow() + .then(handleShellErr); + progress.update("decompile_decompiling", true); + } else progress.update("decompile_decompiling", null); + + // background step + if (process.env.NODE_ENV !== "test" && !commitAnyway) { + const gzFile = "code.js.gz"; + + const gzipper = new Worker(gzipWorkerURL); + progress.start("decompile_gzip"); + gzipper.addEventListener("message", async ({ data }) => { + if (data === true) { + await commit([gzFile], `chore: update decompiled code for ${cuteVersion}`); + progress.update("decompile_gzip", true); + } + gzipper.terminate(); + }); + gzipper.postMessage({ path: codePath, target: join("../data", gzFile) }); + } else { + progress.update("decompile_gzip", null); + } +} diff --git a/src/update/tasks/diffs.ts b/src/update/tasks/diffs.ts new file mode 100644 index 00000000000..14a0ac27dca --- /dev/null +++ b/src/update/tasks/diffs.ts @@ -0,0 +1,290 @@ +import { + type CodeDiff, + type Diff, + type Differs, + DiffType, + type Icons, + type RawColors, + type Semantic, + type SemanticColors, +} from "../../types"; +import { fetchGitChanged, gitChanged } from "../git"; +import { type Progress, wrapPromise } from "../progress"; +import { diffAnyway, prevFiles } from "../shared"; +import { join, parseJsonl } from "../utils"; + +async function diffRaw() { + if (!prevFiles.has("raw.json")) { + throw new Error("Missing prevFile: raw.json"); + } + + const oldRaw = JSON.parse(new TextDecoder().decode(prevFiles.get("raw.json"))) as RawColors; + const newRaw: RawColors = await Bun.file(join("../data", "raw.json")).json(); + + const renamed = new Set(); + const changes = new Map(); + for (const [name, source] of Object.entries(newRaw)) + if (!oldRaw[name]) { + const oldName = Object.entries(oldRaw).find(([key, val]) => !newRaw[key] && val === source)?.[0]; + + if (!oldName) + changes.set(name, { + type: DiffType.Added, + source, + }); + else { + renamed.add(oldName); + changes.set(name, { + type: DiffType.Renamed, + oldName, + source, + }); + } + } else if (oldRaw[name].toLowerCase() !== source.toLowerCase()) + changes.set(name, { + type: DiffType.Changed, + source, + oldSource: oldRaw[name], + }); + for (const [name, source] of Object.entries(oldRaw)) + if (!(newRaw[name] || renamed.has(name))) + changes.set(name, { + type: DiffType.Removed, + source, + }); + + return changes; +} + +function getSemanticLabel(semantic: Semantic) { + const light = semantic.light, + dark = semantic.darker ?? semantic.dark; + if (dark && light) return `☀️ ${light[0]}, 🌙 ${dark[0]}`; + return Object.entries(semantic) + .map(([name, [color]]) => `${name} ${color}`) + .join(", "); +} + +async function diffSemantic() { + if (!prevFiles.has("semantic.json")) { + throw new Error("Missing prevFile: semantic.json"); + } + + const oldSemantic = JSON.parse(new TextDecoder().decode(prevFiles.get("semantic.json"))) as SemanticColors; + const newSemantic: SemanticColors = await Bun.file(join("../data", "semantic.json")).json(); + + const renamed = new Set(); + const changes = new Map(); + for (const [name, semantic] of Object.entries(newSemantic)) { + const source = Object.values(semantic) + .map(([color]) => color) + .join(","); + if (!oldSemantic[name]) { + const themes = Object.keys(semantic).length; + const oldName = Object.entries(oldSemantic).find( + ([key, val]) => + !newSemantic[key] && + Object.keys(val).length === themes && + Object.entries(val).every(([skey, sval]) => sval[0] === semantic[skey][0]), + )?.[0]; + + if (!oldName) + changes.set(name, { + type: DiffType.Added, + source, + label: getSemanticLabel(semantic), + }); + else { + renamed.add(oldName); + changes.set(name, { + type: DiffType.Renamed, + oldName, + source, + }); + } + } else + for (const [theme, [color]] of Object.entries(semantic)) + if (!oldSemantic[name][theme]) + changes.set(`${name}.${theme}`, { + type: DiffType.Added, + source: color, + }); + else if (oldSemantic[name][theme][0].toLowerCase() !== color.toLowerCase()) + changes.set(`${name}.${theme}`, { + type: DiffType.Changed, + source: color, + oldSource: oldSemantic[name][theme][0], + }); + } + for (const [name, semantic] of Object.entries(oldSemantic)) { + if (renamed.has(name)) continue; + + const source = Object.values(semantic) + .map(([color]) => color) + .join(","); + if (!newSemantic[name]) + changes.set(name, { + type: DiffType.Removed, + source, + label: getSemanticLabel(semantic), + }); + else + for (const [theme, [source]] of Object.entries(semantic)) + if (!newSemantic[name][theme]) + changes.set(`${name}.${theme}`, { + type: DiffType.Removed, + source, + }); + } + + return changes; +} + +async function diffIcons() { + if (!prevFiles.has("icons.json")) { + throw new Error("Missing prevFile: icons.json"); + } + + const oldIcons = JSON.parse(new TextDecoder().decode(prevFiles.get("icons.json"))) as Icons; + const newIcons: Icons = await Bun.file(join("../data", "icons.json")).json(); + + const iconDir = { + old: join("../data", "oldicons"), + new: join("../data", "icons"), + }; + + const renamed = new Set(); + const changes = new Map(); + for (const [name, icon] of Object.entries(newIcons)) { + const label = icon.hash.slice(0, 8); + if (!oldIcons[name]) { + const oldName = Object.entries(oldIcons).find(([key, val]) => !newIcons[key] && val.hash === icon.hash)?.[0]; + + if (!oldName) + changes.set(name, { + type: DiffType.Added, + source: join(iconDir.new, icon.file), + label, + }); + else { + renamed.add(oldName); + changes.set(name, { + type: DiffType.Renamed, + oldName, + source: join(iconDir.new, icon.file), + }); + } + } else if (oldIcons[name].hash !== icon.hash) + changes.set(name, { + type: DiffType.Changed, + source: join(iconDir.new, icon.file), + label, + oldSource: join(iconDir.old, oldIcons[name].file), + oldLabel: oldIcons[name].hash.slice(0, 8), + }); + } + for (const [name, icon] of Object.entries(oldIcons)) + if (!(newIcons[name] || renamed.has(name))) + changes.set(name, { + type: DiffType.Removed, + source: join(iconDir.old, icon.file), + label: icon.hash.slice(0, 8), + }); + + return changes; +} + +function parseSource(text: string) { + const source = parseJsonl(text) as { file: string; size: number }[]; + return Object.fromEntries( + source.map(({ file, size }) => [file, { size, compSize: size - JSON.stringify(file).length }]), + ) as Record; +} + +async function diffCode() { + if (!prevFiles.has("source.jsonl")) { + throw new Error("Missing prevFile: source.jsonl"); + } + + const oldCode = parseSource(new TextDecoder().decode(prevFiles.get("source.jsonl"))); + const newCode = parseSource(await Bun.file(join("../data", "source.jsonl")).text()); + + const renamed = new Set(); + const changes = new Map(); + for (const [name, { size, compSize }] of Object.entries(newCode)) { + if (!oldCode[name]) { + const oldName = Object.entries(oldCode).find(([key, val]) => !newCode[key] && val.compSize === compSize)?.[0]; + + if (!oldName) + changes.set(name, { + type: DiffType.Added, + size, + }); + else { + renamed.add(oldName); + changes.set(name, { + type: DiffType.Renamed, + oldName, + size, + }); + } + } + } + for (const [name, { size }] of Object.entries(oldCode)) + if (!(newCode[name] || renamed.has(name))) + changes.set(name, { + type: DiffType.Removed, + size, + }); + + return changes; +} + +export default async function diffs(progress: Progress) { + await fetchGitChanged(); + + if (!(gitChanged.has("version.txt") || diffAnyway)) { + progress.update("diff", null); + progress.update("diff_raw", null); + progress.update("diff_semantic", null); + progress.update("diff_icons", null); + progress.update("diff_code", null); + return; + } + + const differs = {} as Differs; + + await Promise.all([ + gitChanged.has("raw.json") + ? wrapPromise( + diffRaw().then((x) => (differs.raw = x)), + progress, + "diff_raw", + ) + : progress.update("diff_raw", null), + gitChanged.has("semantic.json") + ? wrapPromise( + diffSemantic().then((x) => (differs.semantic = x)), + progress, + "diff_semantic", + ) + : progress.update("diff_semantic", null), + gitChanged.has("icons.json") + ? wrapPromise( + diffIcons().then((x) => (differs.icons = x)), + progress, + "diff_icons", + ) + : progress.update("diff_icons", null), + gitChanged.has("source.jsonl") + ? wrapPromise( + diffCode().then((x) => (differs.code = x)), + progress, + "diff_code", + ) + : progress.update("diff_code", null), + ]); + + progress.update("diff", true); + return differs; +} diff --git a/src/update/tasks/icons.test.ts b/src/update/tasks/icons.test.ts new file mode 100644 index 00000000000..d48262aed2a --- /dev/null +++ b/src/update/tasks/icons.test.ts @@ -0,0 +1,10 @@ +import { sortObj } from "../utils"; +import { parseAssets } from "./icons"; + +const codeFile = Bun.file("tmp/code.js"); +if (!(await codeFile.exists())) throw "Missing decompiled code.js"; + +const code = (await codeFile.text()).replace(/\r/g, "").split("\n"); + +const { icons } = await parseAssets(code); +await Bun.write("temp/icons.json", JSON.stringify(sortObj(icons), undefined, 4)); diff --git a/src/update/tasks/icons.ts b/src/update/tasks/icons.ts new file mode 100644 index 00000000000..6d62b22ca90 --- /dev/null +++ b/src/update/tasks/icons.ts @@ -0,0 +1,114 @@ +import { copyFile, mkdir, readdir, rename } from "node:fs/promises"; +import { basename } from "node:path"; +import { runInNewContext } from "node:vm"; +import type { Icons } from "../../types"; +import { commit } from "../git"; +import type { Progress } from "../progress"; +import { apkSplits, apksFolder, cuteVersion } from "../shared"; +import { discordPath, join, listRequiredDirs, sortObj } from "../utils"; + +const infoObjRegex = /({.+?})/; +const scalesArrayRegex = /(\[.+?\])/; + +// a bit less scary code matching below, be warned +export async function parseAssets(code: string[]) { + const retrievedAssets: { + httpServerLocation: string; + width: number; + height: number; + scales: number[]; + hash: string; + name: string; + type: "png" | "svg" | "lottie"; + }[] = []; + + for (let i = 0; i < code.length; i++) { + // next line has asset info, after that are scales + const line = code[i], + infoLine = code[i + 1], + scalesLine = code[i + 2]; + + if (line.includes(".registerAsset") && infoLine?.includes("'httpServerLocation'") && scalesLine?.includes("[")) { + const infoText = infoLine.match(infoObjRegex)?.[1]; + if (!infoText) throw new Error(`Failed to find infoText for ${infoLine} (line ${i + 1})`); + + const scalesText = scalesLine.match(scalesArrayRegex)?.[1]; + if (!scalesText) throw new Error(`Failed to find scalesText for ${scalesLine} (line ${i + 2})`); + + const info = runInNewContext(`(${infoText})`); + if (typeof info !== "object") continue; + + const scales = runInNewContext(`(${scalesText})`); + if (!Array.isArray(scales)) continue; + + info.scales = scales; + + if ( + ["httpServerLocation", "hash", "name", "type"].every((x) => x in info) && + ["svg", "png", "lottie"].includes(info.type) && + !info.httpServerLocation.includes("node_modules/.pnpm") + ) { + retrievedAssets.push(info); + } + } + } + + // get files of all apks by hierarchy + const apkPaths = new Map(); + for (const split of apkSplits) { + const folder = join(apksFolder, split); + for (const path of await readdir(folder, { recursive: true })) apkPaths.set(basename(path), join(folder, path)); + } + + const icons: Icons = {}; + const toCopy: { from: string; to: string }[] = []; + for (const asset of retrievedAssets) { + const baseName = [...asset.httpServerLocation.split("/").slice(2), asset.name] + .join("_") + .toLowerCase() + .replace(/\W+/g, ""); + const path = `${baseName}.${asset.type}`; + + const apkPath = apkPaths.get(path); + if (apkPath) { + const realPath = join( + discordPath(asset.httpServerLocation.split("/").slice(2).join("/")), + `${asset.name}.${asset.type}`, + ); + + icons[asset.name] = { + file: realPath, + hash: asset.hash, + scales: asset.scales, + width: asset.width ?? null, + height: asset.height ?? null, + }; + toCopy.push({ + from: apkPath, + to: join("../data/icons", realPath), + }); + } + } + + return { icons, toCopy }; +} + +export default async function icons(progress: Progress, code: string[]) { + progress.start("icons_getting"); + + const { icons, toCopy } = await parseAssets(code); + await Bun.write("../data/icons.json", JSON.stringify(sortObj(icons), undefined, 4)); + progress.update("icons_getting", true); + + progress.start("icons_copying"); + + // don't delete the old icons folder so we can display the old images + await rename("../data/icons", "../data/oldicons"); + const dirs = listRequiredDirs(toCopy.map((x) => x.to)); + + await Promise.all(dirs.map((dir) => mkdir(dir, { recursive: true }))); + await Promise.all(toCopy.map(({ from, to }) => copyFile(from, to))); + + await commit(["icons.json", "icons"], `chore: update icons for ${cuteVersion}`); + progress.update("icons_copying", true); +} diff --git a/src/update/tasks/webhook.ts b/src/update/tasks/webhook.ts new file mode 100644 index 00000000000..5761e44cde8 --- /dev/null +++ b/src/update/tasks/webhook.ts @@ -0,0 +1,271 @@ +import { basename } from "node:path"; +import type { Canvas } from "@napi-rs/canvas"; +import { + type APIMessageTopLevelComponent, + ComponentType, + MessageFlags, + type RESTPostAPIWebhookWithTokenJSONBody, + RouteBases, + Routes, +} from "discord-api-types/v10"; +import { drawSections } from "../../canvas"; +import { makeSections } from "../../canvas/factory"; +import { type CodeDiff, type Diff, type Differs, DiffType } from "../../types"; +import { cuteVersion, maxDiffChanges, version } from "../shared"; +import { assert, formatBytes, sortEntries } from "../utils"; + +function fileBase(path: string, other?: string) { + if (!other) return `... ${basename(path)}`; + const paths = path.split("/"), + others = other.split("/"); + + let offset = 0; + while (offset < Math.min(paths.length, others.length) - 1 && paths[offset] === others[offset]) { + offset++; + } + + return `... ${paths.slice(offset).join("/")}`; +} + +function makeFooter(size: number, item: string) { + return `**${size.toLocaleString("en-US")}** ${item} change${size !== 1 ? "s" : ""} total`; +} + +function getThreshold(lines: string[]) { + return lines.some((x) => x.includes("\n")) ? Math.floor(maxDiffChanges / 2) : maxDiffChanges; +} + +function cap(lines: string[], item: string) { + const threshold = getThreshold(lines); + if (lines.length > threshold) + return [...lines.slice(0, threshold), `(+${(lines.length - threshold).toLocaleString("en-US")} ${item})`]; + return lines; +} + +interface FormattedDiff { + embed: string[]; + file: string; + capped: boolean; +} + +function formatDiff(diffs: Map): FormattedDiff { + const entries = [...diffs.entries()]; + + const sections = { + Added: sortEntries(entries) + .map( + ([name, diff]) => + diff.type === DiffType.Added && + ("size" in diff + ? `+ ${fileBase(name)} (${formatBytes(diff.size)})` + : `+ ${name}: ${diff.label || diff.source}`), + ) + .filter((x) => typeof x === "string"), + Changed: sortEntries(entries) + .map( + ([name, diff]) => + diff.type === DiffType.Changed && + `- ${name}: ${diff.oldLabel || diff.oldSource}\n+ ${name}: ${diff.label || diff.source}`, + ) + .filter((x) => typeof x === "string"), + Renamed: sortEntries(entries) + .map( + ([name, diff]) => + diff.type === DiffType.Renamed && + ("size" in diff + ? `- ${fileBase(diff.oldName, name)}\n+ ${fileBase(name, diff.oldName)}` + : `- ${diff.oldName}\n+ ${name}`), + ) + .filter((x) => typeof x === "string"), + Removed: sortEntries(entries) + .map( + ([name, diff]) => + diff.type === DiffType.Removed && + ("size" in diff + ? `- ${fileBase(name)} (${formatBytes(diff.size)})` + : `- ${name}: ${diff.label || diff.source}`), + ) + .filter((x) => typeof x === "string"), + }; + + const text = Object.entries(sections).filter(([, lines]) => lines.length); + return { + embed: text.map( + ([title, lines]) => `### ${title}\n\`\`\`diff\n${cap(lines, title.toLowerCase()).join("\n")}\`\`\``, + ), + file: text.map(([title, lines]) => `# ${title}\n\n${lines.join("\n")}`).join("\n\n"), + capped: text.some(([, lines]) => lines.length > getThreshold(lines)), + }; +} + +interface WebhookEmbed { + title: string; + body: FormattedDiff; + image?: Canvas; + footer: string; + key: string; +} + +async function sendWebhook(webhook: string, role: string, embeds: WebhookEmbed[], color?: number) { + const body = new FormData(); + const images = new Map(), + diffs = new Map(); + + let fi = 0; + for (let i = 0; i < embeds.length; i++) { + const embed = embeds[i]; + if (embed.image) { + const key = fi++; + const filename = `${embed.key}.png`; + images.set(i, filename); + + body.append(`files[${key}]`, new Blob([embed.image.toBuffer("image/png")], { type: "image/png" }), filename); + } + if (embed.body.capped) { + const key = fi++; + const filename = `${embed.key}-changes.diff`; + diffs.set(i, filename); + + body.append(`files[${key}]`, new Blob([embed.body.file], { type: "text/plain" }), filename); + } + } + + body.append( + "payload_json", + JSON.stringify({ + flags: MessageFlags.IsComponentsV2, + components: [ + { + type: ComponentType.TextDisplay, + content: `<@&${role}>`, + }, + ...embeds.map( + ({ title, body, image, footer }, i) => + ({ + type: ComponentType.Container, + accent_color: color, + components: [ + { + type: ComponentType.TextDisplay, + content: `## ${title}\n-# ${version} (${cuteVersion})`, + }, + ...body.embed.map((content) => ({ + type: ComponentType.TextDisplay, + content, + })), + body.capped && { + type: ComponentType.File, + file: { + url: `attachment://${diffs.get(i)}`, + }, + }, + body.capped && { + type: ComponentType.Separator, + }, + image && { + type: ComponentType.MediaGallery, + items: [ + { + media: { + url: `attachment://${images.get(i)}`, + }, + }, + ], + }, + { + type: ComponentType.TextDisplay, + content: footer, + }, + ].filter((x) => !!x), + }) as APIMessageTopLevelComponent, + ), + ], + allowed_mentions: process.env.NODE_ENV === "test" ? { parse: [] } : { roles: [role] }, + } as RESTPostAPIWebhookWithTokenJSONBody), + ); + + const url = new URL(webhook); + url.searchParams.set("wait", "true"); + url.searchParams.set("with_components", "true"); + + const res = await fetch(url, { + method: "POST", + body, + }); + + const data = await res.text(); + if (!res.ok) + throw new Error(`Failed to send webhook message with embeds ${embeds.map((x) => x.title).join(", ")}: ${data}`); + + const discordToken = process.env.bot_token; + if (!discordToken) return; + + // this can fail quietly + try { + const message = JSON.parse(data); + + const route = `${RouteBases.api}/${Routes.channelMessageCrosspost(message.channel_id, message.id)}`; + await fetch(route, { + method: "POST", + headers: { + Authorization: discordToken, + }, + }); + } catch {} +} + +export async function webhook(diffs: Differs) { + if (diffs.raw?.size || diffs.semantic?.size) + await sendWebhook( + assert(process.env.color_webhook, "Missing color webhook env"), + "1227327297795657850", + [ + diffs.raw?.size && { + title: "Raw colors", + body: formatDiff(diffs.raw), + image: drawSections(await makeSections(diffs.raw)), + footer: makeFooter(diffs.raw.size, "raw color"), + key: "raw-colors", + }, + diffs.semantic?.size && { + title: "Semantic colors", + body: formatDiff(diffs.semantic), + image: drawSections(await makeSections(diffs.semantic)), + footer: makeFooter(diffs.semantic.size, "semantic color"), + key: "semantic-colors", + }, + ].filter((x) => !!x), + 0xd8ef9e, + ); + + if (diffs.icons?.size) + await sendWebhook( + assert(process.env.icons_webhook, "Missing icons webhook env"), + "1227327765079003217", + [ + { + title: "Icons", + body: formatDiff(diffs.icons), + image: drawSections(await makeSections(diffs.icons, true)), + footer: makeFooter(diffs.icons.size, "icon"), + key: "icons", + }, + ], + 0xf4b8f7, + ); + + if (diffs.code?.size) + await sendWebhook( + assert(process.env.code_webhook, "Missing code webhook env"), + "1233861867059941387", + [ + { + title: "Code", + body: formatDiff(diffs.code), + footer: makeFooter(diffs.code.size, "code"), + key: "code", + }, + ], + 0x86faf3, + ); +} diff --git a/src/update/utils.ts b/src/update/utils.ts new file mode 100644 index 00000000000..065a79af8f0 --- /dev/null +++ b/src/update/utils.ts @@ -0,0 +1,74 @@ +import { join as _join, dirname } from "node:path"; +import type { $ } from "bun"; + +export function assert(value: T | undefined | null, message?: string): T { + if (value === undefined || value === null) + throw new TypeError(message || `Failed to assert that ${value} is defined`); + return value; +} + +const numberSuffixRegex = /(.*?)(\d*)$/; +export function sortEntries(entries: T) { + return entries.sort(([a], [b]) => { + const [, _aKey, aNum] = a.match(numberSuffixRegex) || [], + [, _bKey, bNum] = b.match(numberSuffixRegex) || []; + const aKey = _aKey ?? a, + bKey = _bKey ?? b; + + if (aKey === bKey && aNum && bNum) return Number(aNum) - Number(bNum); + return aKey.localeCompare(bKey); + }); +} + +export function sortObj(obj: T) { + return Object.fromEntries(sortEntries(Object.entries(obj))) as T; +} + +// "you should make it uppercase" +const BYTE = 1024; +export function formatBytes(bytes: number, decimals = 1) { + if (!bytes) return "0 B"; + + const sizes = ["B", "KB", "MB"]; + const mag = Math.min(Math.floor(Math.log(bytes) / Math.log(BYTE)), sizes.length - 1); + return `${parseFloat((bytes / BYTE ** mag).toFixed(decimals))} ${sizes[mag]}`; +} + +export function handleShellErr(out: $.ShellOutput): $.ShellOutput { + if (out.exitCode !== 0 && out.exitCode !== 11) + throw new Error( + `${`${out.stdout.toString().trim()}\n${out.stderr.toString().trim()}`} (exit code ${out.exitCode})`, + ); + return out; +} + +export function formatError(e: any) { + return e?.stack ?? e?.message ?? String(e); +} + +export function listRequiredDirs(files: string[]) { + const dirs = [...new Set(files.map(dirname))].sort(); + + const required: string[] = []; + for (let i = 0; i < dirs.length; i++) { + if (i === dirs.length - 1 || !dirs[i + 1].startsWith(`${dirs[i]}/`)) { + required.push(dirs[i]); + } + } + return required; +} + +export function parseJsonl(text: string) { + const lines = text + .trim() + .split("\n") + .map((x) => x.trim()) + .filter((x) => !!x); + return JSON.parse(`[${lines.join()}]`); +} + +export function discordPath(path: string) { + return join("app", path); +} + +export const join = (...paths: string[]) => _join(...paths).replace(/\\/g, "/"); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..128870efae6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + "types": ["bun"], + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": false, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + }, + "exclude": ["tmp"] +}