From 0b8db88e2b34ae5c8b6b12496037a358bc48b154 Mon Sep 17 00:00:00 2001 From: sophie Date: Mon, 1 Jul 2024 13:30:52 +0300 Subject: [PATCH] first commit --- .gitignore | 177 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 12 +++ advancementAPI.ts | 77 +++++++++++++++++++ bun.lockb | Bin 0 -> 17861 bytes config.example.json | 8 ++ index.ts | 62 ++++++++++++++++ package.json | 19 +++++ tsconfig.json | 27 +++++++ 8 files changed, 382 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 advancementAPI.ts create mode 100755 bun.lockb create mode 100644 config.example.json create mode 100644 index.ts create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c686e05 --- /dev/null +++ b/.gitignore @@ -0,0 +1,177 @@ +# 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 +config.json +minecraft.otf \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a941ca6 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# see_leaderboard_bot + +Please set your configuration options in `config.json`. Copy `config.example.json` and make it config.json and replace everything. +Add minecraft's font as `minecraft.otf`. +Run index.ts (also `bun i`). Write ".lb" in chat and it'll show you the leaderboard. + +This is made for BACAP's "bac_advancements" leaderboard. +Just change all mentions of "Advancements" or "bac_advancements" and you'll have a solution. + +This app uses the whitelist to get every player that's playing. If you have a better idea, contact me with my socials from [sad.ovh](https://sad.ovh). You can also modify the AdvancementAPI to make it work differently. To your choosing. + +This project was created using `bun init` in bun v1.1.13. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/advancementAPI.ts b/advancementAPI.ts new file mode 100644 index 0000000..7a4f83f --- /dev/null +++ b/advancementAPI.ts @@ -0,0 +1,77 @@ +//@ts-nocheck +import Rcon from "ts-rcon"; +import config from "./config.json" assert { type: "json" }; + +const advancements = new Map(); +let advancementsRequired = 0; +let authenicated = false; + +const advancementRegex = /^([^ ]*) has (\d*) \[Advancements\]$/gm; +const playerRegex = /There are \d* whitelisted player\(s\): /gm; +const noAdvancements = + /^Can't get value of bac_advancements for ([^;]*); none is set$/gm; + +const client = new Rcon( + config.rcon.host, + config.rcon.port, + config.rcon.password +); + +client.on("connect", () => { + console.log("RCON connected."); +}); + +client.on("auth", () => { + console.log("RCON authenicated."); + authenicated = true; +}); + +client.on("response", (a: string) => { + if (playerRegex.test(a)) { + const whitelistedPlayers = a.replace(playerRegex, "").split(", "); + advancementsRequired = whitelistedPlayers.length; + + for (let i = 0; i < whitelistedPlayers.length; i++) { + setTimeout(() => { + const name = whitelistedPlayers[i]; + client.send(`scoreboard players get ${name} bac_advancements`, 0x02); + }, i * 20); + } + } + + if (advancementRegex.test(a)) { + advancements.set( + a.replace(advancementRegex, "$1"), + +a.replace(advancementRegex, "$2") + ); + } + if (noAdvancements.test(a)) { + advancements.set(a.replace(noAdvancements, "$1"), 0); + } + if (advancements.size == advancementsRequired) { + client.emit("done"); + } +}); + +export function getAdvancements(): Promise> { + return new Promise(async (res, rej) => { + if (!authenicated) { + await new Promise((res2, rej2) => { + client.once("auth", () => { + res2(""); + }); + client.once("error", () => { + rej2(); + }); + }); + } + advancementsRequired = 0; + advancements.clear(); + client.send("whitelist list", 0x02); + client.once("done", () => { + res(advancements); + }); + }); +} + +client.connect(); diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..5761d1e5383ab27cfc09dc73859c890ae5705c00 GIT binary patch literal 17861 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 { + const canvas = createCanvas(170, 255); + const ctx = canvas.getContext("2d"); + + ctx.fillRect(0, 0, 200, 400); + ctx.fillStyle = "white"; + ctx.font = "15px Minecraft"; + + { + const width = ctx.measureText("Advancements").width; + + ctx.fillText("Advancements", canvas.width / 2 - width / 2, 15); + } + + const advancements = [...(await getAdvancements()).entries()]; + advancements.sort((a, b) => b[1] - a[1]); + + for (let i = 0; i < Math.min(advancements.length, 15); i++) { + const n = advancements[i]; + + // render name + const firstOffset = (i + 2) * 15; + ctx.fillText(n[0], 5, firstOffset); + + // render number + const secondOffset = ctx.measureText("" + n[1]).width; + ctx.fillStyle = "yellow"; + ctx.fillText("" + n[1], canvas.width - secondOffset - 5, firstOffset); + ctx.fillStyle = "white"; + } + + return canvas.toBuffer("image/png"); +} + +client.once(Events.ClientReady, async (readyClient) => { + console.log(`DISCORD connected. ${readyClient.user.tag}`); +}); + +client.on("messageCreate", async (message) => { + if (message.content == ".lb") { + message.channel.send({ + files: [await makeCanvas()], + }); + } +}); + +client.login(config.token); diff --git a/package.json b/package.json new file mode 100644 index 0000000..8aa3a36 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "see_leaderboard_bot", + "module": "index.ts", + "type": "module", + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@napi-rs/canvas": "^0.1.53", + "bufferutil": "^4.0.8", + "discord.js": "^14.15.3", + "ts-rcon": "^1.2.1", + "utf-8-validate": "^6.0.4", + "zlib-sync": "^0.1.9" + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}