Matrix support, chat refactoring, and probably other stuff i forgor about

This commit is contained in:
Chipmunk 2024-03-16 16:03:36 -04:00
parent 4ba2c7b05a
commit a67478cfd9
22 changed files with 752 additions and 64 deletions

2
.gitignore vendored
View file

@ -106,9 +106,9 @@ dist
# Config files
config.js
config.json
config.json5
# Other
config.json5
logs
music
images

5
bot.js
View file

@ -94,9 +94,10 @@ function createBot (options = {}) {
})
})
bot._client = options.client ?? mc.createClient(options)
bot.emit('set_client', bot._client)
for (const plugin of plugins) plugin(bot, options)
for (const plugin of plugins) plugin(bot, options) // Load plugins before emitting set_client so that plugins can listen for the event
bot.emit('set_client', bot._client)
function loadPlugin (plugin) {
try {

View file

@ -4,6 +4,7 @@ const fs = require('fs')
const path = require('path')
const moment = require('moment')
const json5 = require('json5')
const matrix = require('matrix-js-sdk')
if (!fs.existsSync('config.json5')) {
fs.copyFileSync(path.join(__dirname, 'default.json5'), 'config.json5')
@ -31,8 +32,19 @@ const rl = readline.createInterface({
prefix: '> '
})
const matrixClients = {}
for (const key in config.matrixClients) {
const client = matrix.createClient(config.matrixClients[key])
matrixClients[key] = client
client.startClient()
}
for (const options of config.bots) {
const bot = createBot(options)
const mergedOptions = { ...(config.all ?? {}), ...options }
if (mergedOptions.matrix && typeof mergedOptions.matrix.client !== 'object') mergedOptions.matrix.client = matrixClients[mergedOptions.matrix.client]
const bot = createBot(mergedOptions)
bot.on('error', console.error)

329
package-lock.json generated
View file

@ -13,9 +13,11 @@
"fluent-ffmpeg": "^2.1.2",
"json5": "^2.2.3",
"kahoot.js-api": "^2.4.0",
"matrix-js-sdk": "^31.5.0",
"minecraft-protocol": "^1.26.5",
"moment": "^2.29.1",
"prismarine-nbt": "^2.2.0",
"prismarine-registry": "^1.7.0",
"rfb2": "^0.2.2",
"standard": "^16.0.4",
"urban-dictionary": "git+https://code.chipmunk.land/ChipmunkMC/urban-dictionary.git"
@ -74,6 +76,17 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
"integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@eslint/eslintrc": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
@ -128,6 +141,14 @@
"node": ">= 8"
}
},
"node_modules/@matrix-org/matrix-sdk-crypto-wasm": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-4.7.0.tgz",
"integrity": "sha512-07UqoxUolP2RJfXGwN1U9+1wWfHde0SilSzvdOU6RagJqQ3SdKERPBcgp20texOvIl65K57kJ81tD//+mj8skQ==",
"engines": {
"node": ">= 10"
}
},
"node_modules/@mozilla/readability": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.4.1.tgz",
@ -260,6 +281,11 @@
"@types/responselike": "*"
}
},
"node_modules/@types/events": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g=="
},
"node_modules/@types/http-cache-semantics": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
@ -305,6 +331,11 @@
"@types/node": "*"
}
},
"node_modules/@types/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
},
"node_modules/@xboxreplay/errors": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@xboxreplay/errors/-/errors-0.1.0.tgz",
@ -369,6 +400,11 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/another-json": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/another-json/-/another-json-0.2.0.tgz",
"integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg=="
},
"node_modules/ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@ -490,6 +526,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base-x": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
"integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -522,6 +563,14 @@
"version": "1.0.0",
"resolved": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git#c89271d021a1537d3045a93850e0c7ccb6efd9ae"
},
"node_modules/bs58": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
"integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
"dependencies": {
"base-x": "^4.0.0"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
@ -655,6 +704,14 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -2068,6 +2125,14 @@
"safe-buffer": "^5.0.1"
}
},
"node_modules/jwt-decode": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
"engines": {
"node": ">=18"
}
},
"node_modules/kahoot.js-api": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/kahoot.js-api/-/kahoot.js-api-2.4.0.tgz",
@ -2187,6 +2252,18 @@
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
"integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM="
},
"node_modules/loglevel": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz",
"integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==",
"engines": {
"node": ">= 0.6.0"
},
"funding": {
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/loglevel"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -2222,6 +2299,56 @@
"resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.5.3.tgz",
"integrity": "sha512-vGBKTA+jwM4KgjGZ+S/8/Mkj9rWzePyGY6jManXPGhiWu63RYwW8dKPyk5koP+8qNVhPhHgFa1y/MJ4wrjsNrg=="
},
"node_modules/matrix-events-sdk": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz",
"integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
},
"node_modules/matrix-js-sdk": {
"version": "31.5.0",
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-31.5.0.tgz",
"integrity": "sha512-d8Y/Vt6PdX8leSOQ06yoArJ1xMwCzxSb1H2GzW9mtOgXnHpeYvrAuPrYr32k5hfdUAJp0xPibSqDP+/+2kCnpg==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@matrix-org/matrix-sdk-crypto-wasm": "^4.6.0",
"another-json": "^0.2.0",
"bs58": "^5.0.0",
"content-type": "^1.0.4",
"jwt-decode": "^4.0.0",
"loglevel": "^1.7.1",
"matrix-events-sdk": "0.0.1",
"matrix-widget-api": "^1.6.0",
"oidc-client-ts": "^3.0.1",
"p-retry": "4",
"sdp-transform": "^2.14.1",
"unhomoglyph": "^1.0.6",
"uuid": "9"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/matrix-js-sdk/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/matrix-widget-api": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.6.0.tgz",
"integrity": "sha512-VXIJyAZ/WnBmT4C7ePqevgMYGneKMCP/0JuCOqntSsaNlCRHJvwvTxmqUU+ufOpzIF5gYNyIrAjbgrEbK3iqJQ==",
"dependencies": {
"@types/events": "^3.0.0",
"events": "^3.2.0"
}
},
"node_modules/midi-file": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/midi-file/-/midi-file-1.2.2.tgz",
@ -2464,6 +2591,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/oidc-client-ts": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.0.1.tgz",
"integrity": "sha512-xX8unZNtmtw3sOz4FPSqDhkLFnxCDsdo2qhFEH2opgWnF/iXMFoYdBQzkwCxAZVgt3FT3DnuBY3k80EZHT0RYg==",
"dependencies": {
"jwt-decode": "^4.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -2518,6 +2656,18 @@
"node": ">=4"
}
},
"node_modules/p-retry": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
"integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
"dependencies": {
"@types/retry": "0.12.0",
"retry": "^0.13.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
@ -2746,6 +2896,15 @@
"node-fetch": "^2.6.1"
}
},
"node_modules/prismarine-registry": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/prismarine-registry/-/prismarine-registry-1.7.0.tgz",
"integrity": "sha512-yyva0FpWI078nNeMhx8ekVza5uUTYhEv+C+ADu3wUQXiG8qhXkvrf0uzsnhTgZL8BLdsi2axgCEiKw9qSKIuxQ==",
"dependencies": {
"minecraft-data": "^3.0.0",
"prismarine-nbt": "^2.0.0"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -2887,6 +3046,11 @@
"node": ">= 6"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/regexp.prototype.flags": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz",
@ -2958,6 +3122,14 @@
"lowercase-keys": "^2.0.0"
}
},
"node_modules/retry": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
"engines": {
"node": ">= 4"
}
},
"node_modules/rfb2": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/rfb2/-/rfb2-0.2.2.tgz",
@ -2999,6 +3171,14 @@
}
]
},
"node_modules/sdp-transform": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.14.2.tgz",
"integrity": "sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==",
"bin": {
"sdp-verify": "checker.js"
}
},
"node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
@ -3410,6 +3590,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/unhomoglyph": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz",
"integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg=="
},
"node_modules/urban-dictionary": {
"version": "3.0.2",
"resolved": "git+https://code.chipmunk.land/ChipmunkMC/urban-dictionary.git#3b60e3adce74d62f660b2b22a16c5a0084250757",
@ -3595,6 +3780,14 @@
"js-tokens": "^4.0.0"
}
},
"@babel/runtime": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
"integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
"requires": {
"regenerator-runtime": "^0.14.0"
}
},
"@eslint/eslintrc": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
@ -3636,6 +3829,11 @@
"tweetnacl": "^1.0.1"
}
},
"@matrix-org/matrix-sdk-crypto-wasm": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-4.7.0.tgz",
"integrity": "sha512-07UqoxUolP2RJfXGwN1U9+1wWfHde0SilSzvdOU6RagJqQ3SdKERPBcgp20texOvIl65K57kJ81tD//+mj8skQ=="
},
"@mozilla/readability": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.4.1.tgz",
@ -3756,6 +3954,11 @@
"@types/responselike": "*"
}
},
"@types/events": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g=="
},
"@types/http-cache-semantics": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
@ -3803,6 +4006,11 @@
"@types/node": "*"
}
},
"@types/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
},
"@xboxreplay/errors": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@xboxreplay/errors/-/errors-0.1.0.tgz",
@ -3852,6 +4060,11 @@
"uri-js": "^4.2.2"
}
},
"another-json": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/another-json/-/another-json-0.2.0.tgz",
"integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg=="
},
"ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@ -3943,6 +4156,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"base-x": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
"integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -3961,6 +4179,14 @@
"version": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git#c89271d021a1537d3045a93850e0c7ccb6efd9ae",
"from": "brigadier-commands@git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git"
},
"bs58": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
"integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
"requires": {
"base-x": "^4.0.0"
}
},
"buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
@ -4061,6 +4287,11 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -5056,6 +5287,11 @@
"safe-buffer": "^5.0.1"
}
},
"jwt-decode": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="
},
"kahoot.js-api": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/kahoot.js-api/-/kahoot.js-api-2.4.0.tgz",
@ -5163,6 +5399,11 @@
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
"integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM="
},
"loglevel": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz",
"integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg=="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -5189,6 +5430,48 @@
"resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.5.3.tgz",
"integrity": "sha512-vGBKTA+jwM4KgjGZ+S/8/Mkj9rWzePyGY6jManXPGhiWu63RYwW8dKPyk5koP+8qNVhPhHgFa1y/MJ4wrjsNrg=="
},
"matrix-events-sdk": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz",
"integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
},
"matrix-js-sdk": {
"version": "31.5.0",
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-31.5.0.tgz",
"integrity": "sha512-d8Y/Vt6PdX8leSOQ06yoArJ1xMwCzxSb1H2GzW9mtOgXnHpeYvrAuPrYr32k5hfdUAJp0xPibSqDP+/+2kCnpg==",
"requires": {
"@babel/runtime": "^7.12.5",
"@matrix-org/matrix-sdk-crypto-wasm": "^4.6.0",
"another-json": "^0.2.0",
"bs58": "^5.0.0",
"content-type": "^1.0.4",
"jwt-decode": "^4.0.0",
"loglevel": "^1.7.1",
"matrix-events-sdk": "0.0.1",
"matrix-widget-api": "^1.6.0",
"oidc-client-ts": "^3.0.1",
"p-retry": "4",
"sdp-transform": "^2.14.1",
"unhomoglyph": "^1.0.6",
"uuid": "9"
},
"dependencies": {
"uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
}
}
},
"matrix-widget-api": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.6.0.tgz",
"integrity": "sha512-VXIJyAZ/WnBmT4C7ePqevgMYGneKMCP/0JuCOqntSsaNlCRHJvwvTxmqUU+ufOpzIF5gYNyIrAjbgrEbK3iqJQ==",
"requires": {
"@types/events": "^3.0.0",
"events": "^3.2.0"
}
},
"midi-file": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/midi-file/-/midi-file-1.2.2.tgz",
@ -5368,6 +5651,14 @@
"es-abstract": "^1.19.1"
}
},
"oidc-client-ts": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.0.1.tgz",
"integrity": "sha512-xX8unZNtmtw3sOz4FPSqDhkLFnxCDsdo2qhFEH2opgWnF/iXMFoYdBQzkwCxAZVgt3FT3DnuBY3k80EZHT0RYg==",
"requires": {
"jwt-decode": "^4.0.0"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -5410,6 +5701,15 @@
"p-limit": "^1.1.0"
}
},
"p-retry": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
"integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
"requires": {
"@types/retry": "0.12.0",
"retry": "^0.13.1"
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
@ -5580,6 +5880,15 @@
"node-fetch": "^2.6.1"
}
},
"prismarine-registry": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/prismarine-registry/-/prismarine-registry-1.7.0.tgz",
"integrity": "sha512-yyva0FpWI078nNeMhx8ekVza5uUTYhEv+C+ADu3wUQXiG8qhXkvrf0uzsnhTgZL8BLdsi2axgCEiKw9qSKIuxQ==",
"requires": {
"minecraft-data": "^3.0.0",
"prismarine-nbt": "^2.0.0"
}
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -5688,6 +5997,11 @@
"util-deprecate": "^1.0.1"
}
},
"regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"regexp.prototype.flags": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz",
@ -5735,6 +6049,11 @@
"lowercase-keys": "^2.0.0"
}
},
"retry": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
},
"rfb2": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/rfb2/-/rfb2-0.2.2.tgz",
@ -5753,6 +6072,11 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"sdp-transform": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.14.2.tgz",
"integrity": "sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA=="
},
"semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
@ -6050,6 +6374,11 @@
"which-boxed-primitive": "^1.0.2"
}
},
"unhomoglyph": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz",
"integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg=="
},
"urban-dictionary": {
"version": "git+https://code.chipmunk.land/ChipmunkMC/urban-dictionary.git#3b60e3adce74d62f660b2b22a16c5a0084250757",
"from": "urban-dictionary@git+https://code.chipmunk.land/ChipmunkMC/urban-dictionary.git"

View file

@ -8,9 +8,11 @@
"fluent-ffmpeg": "^2.1.2",
"json5": "^2.2.3",
"kahoot.js-api": "^2.4.0",
"matrix-js-sdk": "^31.5.0",
"minecraft-protocol": "^1.26.5",
"moment": "^2.29.1",
"prismarine-nbt": "^2.2.0",
"prismarine-registry": "^1.7.0",
"rfb2": "^0.2.2",
"standard": "^16.0.4",
"urban-dictionary": "git+https://code.chipmunk.land/ChipmunkMC/urban-dictionary.git"

View file

@ -4,13 +4,12 @@ const nbt = require('prismarine-nbt')
// filter the chat
function inject (bot) {
// const mcData = require('minecraft-data')(bot._client.version)
// const { id } = mcData.itemsByName.cod
bot.chatFilter = {
enabled: false,
_lines: Array(100).fill(''),
_filter
}
/*
bot.on('chat', ({ raw }) => {
const filtered = _filter(raw)
bot.chatFilter._lines = [...bot.chatFilter._lines, ...filtered.split('\n')]
@ -23,7 +22,7 @@ function inject (bot) {
slot: 36,
item: {
present: true,
itemId: /* id */1,
itemId: /* id *//*1,
itemCount: 1,
nbtData: nbt.comp({
i: nbt.string('\xa7r' + bot.chatFilter._lines.join('\xa7r\n'))
@ -35,6 +34,7 @@ function inject (bot) {
}, 50)
}
})
*/
}
function _filter (message) {

View file

@ -1,5 +1,5 @@
const parseText = require('./../util/text_parser.js')
const nbt = require('prismarine-nbt')
const { parseNbtText } = require('../util/chat/serialization.js')
const parseText = require('../util/text_parser.js')
function inject (bot) {
bot.chat = {
@ -40,51 +40,32 @@ function inject (bot) {
}
}, 200)
bot.on('chat', (message, packet) => {
bot.console.log(`[${bot.host}] ${message.raw}`)
const msg = message.raw
if (msg.match(/<.*§r> §r.*/g)) {
if (packet.sender === '00000000-0000-0000-0000-000000000000') return
const player = bot.players.find(player => player.uuid === packet.sender)
const message = msg.split('§r> §r')[1]
bot.emit('message', player, message)
} else if (msg.match(/<.*> .*/g)) {
if (packet.sender === '00000000-0000-0000-0000-000000000000') return
const player = bot.players.find(player => player.uuid === packet.sender)
const message = msg.split('> ')[1]
bot.emit('message', player, message)
} else if (msg.match(/.* .*§r:§r §.*/g)) {
if (packet.sender === '00000000-0000-0000-0000-000000000000') return
const player = bot.players.find(player => player.uuid === packet.sender)
const message = msg.split('§r:§r ')[1].substring(2)
bot.emit('message', player, message)
} else if (msg.match(/§.*§b: \/.*/g)) {
let username = msg.split('§b: ')[0]
if (username.startsWith('§b')) { username = username.slice(2) }
const player = bot.players.find(player => player.username === username)
if (player == null) return
const command = msg.split('§b: ')[1]
bot.emit('cspy', player, command)
}
})
bot.on('packet.profileless_chat', (packet) => {
const message = parseText(nbt.simplify(packet.message))
bot.emit('chat', message, packet)
const message = parseNbtText(packet.message)
const senderName = parseNbtText(packet.name)
const type = bot.registry?.chatFormattingById[packet.type]
bot.emit('profileless_chat', { message, senderName, type })
bot.emit('chat', message)
})
bot.on('packet.player_chat', (packet) => {
const message = parseText(packet.unsignedChatContent ? nbt.simplify(packet.unsignedChatContent) : packet.plainMessage)
bot.emit('chat', message, { sender: packet.senderUuid })
bot.on('packet.player_chat', (packet) => {console
const unsigned = parseNbtText(packet.unsignedChatContent)
const sender = bot.players.find(player => player.uuid === packet.senderUuid)
const type = bot.registry?.chatFormattingById[packet.type]
bot.emit('player_chat', { unsigned, sender, type })
bot.emit('chat', unsigned)
})
bot.on('packet.system_chat', (packet) => {
const json = nbt.simplify(packet.content)
if (json?.translate === 'advMode.setCommand.success') return
if (packet.isActionBar) return
const message = parseText(json)
bot.emit('chat', message, packet)
const message = parseNbtText(packet.content)
if (message?.translate === 'advMode.setCommand.success') return
bot.emit('system_chat', message)
bot.emit('chat', message)
})
}

View file

@ -2,8 +2,8 @@ const fs = require('fs')
const path = require('path')
const util = require('util')
const { CommandDispatcher, builder: { LiteralArgumentBuilder: { literal }, RequiredArgumentBuilder: { argument } }, arguments: { StringArgumentType: { greedyString } }, exceptions: { CommandSyntaxException } } = require('brigadier-commands')
const CommandSource = require('../util/command_source')
const TextMessage = require('../util/text_message')
const CommandSource = require('../util/command/command_source')
const TextMessage = require('../util/command/text_message')
function inject (bot) {
bot.commands = {
@ -16,6 +16,7 @@ function inject (bot) {
isValid
}
/*
bot.on('message', (player, message) => {
if (!message.startsWith(bot.prefix)) return
@ -24,6 +25,7 @@ function inject (bot) {
}
bot.commands.execute(message.substring(bot.prefix.length), new CommandSource({ bot, player, sendFeedback }))
})
*/
function add (command) {
if (command.register) {

View file

@ -1,7 +1,7 @@
const fs = require('fs')
const util = require('util')
const moment = require('moment')
const CommandSource = require('../util/command_source')
const CommandSource = require('../util/command/command_source')
const parseText = require('../util/text_parser')
const ansimap = {
0: '\x1b[0m\x1b[30m',
@ -82,7 +82,7 @@ function inject (bot) {
}
}
function sendFeedback (message) {
function sendFeedback (text, sendFeedback) {
const { raw } = parseText(message)
bot.console.log(raw)
}

View file

@ -2,9 +2,6 @@ const nbt = require('prismarine-nbt')
const mcNamespace = 'minecraft:'
function inject (bot) {
let mcData = require('minecraft-data')('1.17.1')
bot.on('login', () => (mcData = require('minecraft-data')(bot._client.version)))
bot.core = {
size: { start: { x: 0, y: 0, z: 0 }, end: { x: 15, y: 0, z: 15 } },
@ -16,7 +13,7 @@ function inject (bot) {
refill () {
const refillCommand = `/fill ${this.start.x} ${this.start.y} ${this.start.z} ${this.end.x} ${this.end.y} ${this.end.z} repeating_command_block{CustomName:'""'}`
const location = { x: Math.floor(bot.position.x), y: Math.floor(bot.position.y) - 1, z: Math.floor(bot.position.z) }
const commandBlockId = mcData?.itemsByName.command_block.id
const commandBlockId = bot.registry?.itemsByName.command_block.id
bot._client.write('set_creative_slot', {
slot: 36,

View file

@ -1,14 +1,14 @@
function inject (bot) {
bot.fancyMsg = function (rank, username, message) {
bot.core.run(`/tellraw @a ${JSON.stringify([
bot.tellraw([
{ text: '', color: 'gray' },
{ text: '[', color: 'dark_gray' },
rank,
{ text: '] ', color: 'dark_gray' },
{ text: username, color: bot.colors.secondary },
[{ text: '', color: bot.colors.secondary }, username],
{ text: ' ', color: 'dark_gray' },
message
])}`)
], '@a')
}
}

92
plugins/matrix.js Normal file
View file

@ -0,0 +1,92 @@
const matrix = require('matrix-js-sdk')
const htmlStringify = require('../util/chat/html_stringifier')
const CommandSource = require('../util/command/command_source')
function inject (bot, options) {
if (!options.matrix?.enabled) return
bot.matrix = {
client: options.matrix.client ?? matrix.createClient(options.matrix),
roomId: options.matrix.roomId,
commandPrefix: options.matrix.commandPrefix
}
bot.on('chat', async message => {
sendMessage(message)
})
bot.matrix.client.on('Room.timeline', (event, room, toStartOfTimeline) => {
if (event.getRoomId() !== bot.matrix.roomId || event.getType() !== 'm.room.message' || event.sender.userId === bot.matrix.client.getUserId()) return
const content = event.getContent()
let message = content.body
if (content.url) {
message = {
text: '[Attachment]',
color: bot.colors.primary,
clickEvent: {
action: 'open_url',
value: bot.matrix.client.mxcUrlToHttp(content.url)
}
}
} else if (message.startsWith(bot.matrix.commandPrefix)) {
const source = new CommandSource({ bot, permissionLevel: 1, sendFeedback })
bot.commands.execute(message.substring(bot.matrix.commandPrefix.length), source)
return
}
const senderText = {
text: String(event.sender.rawDisplayName || event.sender.name || event.sender.userId),
hoverEvent: {
action: 'show_text',
contents: ['User ID: ', String(event.sender.userId), '\nClick to copy the user id']
},
clickEvent: {
action: 'copy_to_clipboard',
value: String(event.sender.userId)
}
}
bot.fancyMsg('ChipmunkBot Matrix', senderText, message)
})
let dequeuingMessages = false
let queue = []
async function sendMessage (message) {
const html = htmlStringify(message, { lang: bot.registry.language })
queue.push(html)
if (dequeuingMessages) return
dequeuingMessages = true
while (queue.length !== 0) {
const html = queue.join('<br>')
queue = []
await _sendMessage(html)
}
dequeuingMessages = false
}
async function _sendMessage (html) {
const content = {
msgtype: 'm.text',
format: 'org.matrix.custom.html',
body: html,
formatted_body: html,
'm.mentions': {}
}
try {
await bot.matrix.client.sendEvent(bot.matrix.roomId, 'm.room.message', content, '')
} catch (error) {
console.error('Unable to send Matrix message:', error)
}
}
function sendFeedback (text, sendFeedback) {
sendMessage(text)
}
}
module.exports = inject

View file

@ -1,4 +1,4 @@
const nbt = require('prismarine-nbt')
const { parseNbtText } = require('../util/chat/serialization.js')
const gamemodes = ['survival', 'creative', 'adventure', 'spectator']
@ -67,7 +67,7 @@ function inject (bot) {
const target = bot.players.find(_player => _player.uuid === player.uuid)
if (target == null) return
target.displayName = nbt.simplify(player.displayName)
target.displayName = parseNbtText(player.displayName)
}
async function removePlayer (uuid) {

23
plugins/registry.js Normal file
View file

@ -0,0 +1,23 @@
const loadRegistry = require('prismarine-registry')
function inject (bot) {
function next () {
bot.registry = loadRegistry(bot._client.version)
bot.emit('registry_loaded')
}
bot.on('set_client', client => {
if (client.wait_connect) client.once('connect_allowed', next)
else next()
})
bot.on('packet.login', packet => {
if (packet.dimensionCodec) bot.registry.loadDimensionCodec(packet.dimensionCodec)
})
bot.on('packet.registry_data', packet => {
bot.registry.loadDimensionCodec(packet.codec)
})
}
module.exports = inject

9
plugins/tellraw.js Normal file
View file

@ -0,0 +1,9 @@
function inject (bot) {
function tellraw (text, target = '@a') {
bot.core.run(`minecraft:tellraw ${target} ${JSON.stringify(text)}`)
}
bot.tellraw = tellraw
}
module.exports = inject

View file

@ -0,0 +1,79 @@
const { colors, formatting, reset } = require('./formatting.json')
function escapeSequenceStringify (text, { sequences, lang = {}, parent } = {}) {
text = normalize(text)
let rootText = false
if (parent == null) {
parent = {}
rootText = true
}
let formattingString = ''
formattingString += sequences.colors[text.color ?? parent.color] || ''
if (text.bold ?? parent.bold) formattingString += sequences.formatting.bold
if (text.italic ?? parent.italic) formattingString += sequences.formatting.italic
if (text.underlined ?? parent.underlined) formattingString += sequences.formatting.underlined
if (text.strikethrough ?? parent.strikethrough) formattingString += sequences.formatting.strikethrough
if (text.obfuscated ?? parent.obfuscated) formattingString += sequences.formatting.obfuscated
if (!formattingString && !rootText) formattingString = sequences.reset
let string = formattingString
if (text.text != null) string += text.text
if (text.translate != null) {
let format
if (Object.hasOwn(lang, text.translate)) format = lang[text.translate]
else if (text.fallback != null) format = text.fallback
else format = text.translate
const _with = text.with || []
string += format.replace(/%(?:(\d+)\$)?(s|%)/g, (g0, g1) => {
if (g0 === '%%') return '%'
const idx = g1 ? parseInt(g1) : i++
if (_with[idx]) {
return escapeSequenceStringify(_with[idx], { sequences, lang, parent: text }) + formattingString
}
return ''
})
}
if (text.selector != null) string += text.selector
if (text.keybind) {
// TODO
}
if (text.extra) {
for (const extra of text.extra) {
string += escapeSequenceStringify(extra, { sequences, lang, parent: text })
}
}
return string
}
const colorCodeMapper = colors => Object.fromEntries(colors.map(color => [color.name, color.code]))
const colorCodeSequences = { colors: colorCodeMapper(colors), formatting: colorCodeMapper(formatting), reset: reset.code }
const colorCodeStringify = (text, { lang }) => escapeSequenceStringify(text, { sequences: colorCodeSequences, lang })
const ansiMapper = colors => Object.fromEntries(colors.map(color => [color.name, color.ansi]))
const ansiSequences = { colors: ansiMapper(colors), formatting: ansiMapper(formatting), reset: reset.ansi }
const ansiSringify = (text, { lang }) => escapeSequenceStringify(text, { sequences: ansiSequences, lang })
function normalize (text) {
if (typeof text === 'string') return { text }
if (Array.isArray(text)) {
const text2 = [...text]
const text3 = { ...normalize(text2.shift()) }
text3.extra = text2
return text3
}
return text
}
module.exports = { escapeSequenceStringify, colorCodeStringify, ansiSringify }

28
util/chat/formatting.json Normal file
View file

@ -0,0 +1,28 @@
{
"colors": [
{"name": "black", "code": "§0", "ansi": "\u001b[0m\u001b[30m", "rgb": 0},
{"name": "dark_blue", "code": "§1", "ansi": "\u001b[0m\u001b[34m", "rgb": 170},
{"name": "dark_green", "code": "§2", "ansi": "\u001b[0m\u001b[32m", "rgb": 43520},
{"name": "dark_aqua", "code": "§3", "ansi": "\u001b[0m\u001b[36m", "rgb": 43690},
{"name": "dark_red", "code": "§4", "ansi": "\u001b[0m\u001b[31m", "rgb": 11141120},
{"name": "dark_purple", "code": "§5", "ansi": "\u001b[0m\u001b[35m", "rgb": 11141290},
{"name": "gold", "code": "§6", "ansi": "\u001b[0m\u001b[33m", "rgb": 16755200},
{"name": "gray", "code": "§7", "ansi": "\u001b[0m\u001b[37m", "rgb": 11184810},
{"name": "dark_gray", "code": "§8", "ansi": "\u001b[0m\u001b[90m", "rgb": 5592405},
{"name": "blue", "code": "§9", "ansi": "\u001b[0m\u001b[94m", "rgb": 5592575},
{"name": "green", "code": "§a", "ansi": "\u001b[0m\u001b[92m", "rgb": 5635925},
{"name": "aqua", "code": "§b", "ansi": "\u001b[0m\u001b[96m", "rgb": 5636095},
{"name": "red", "code": "§c", "ansi": "\u001b[0m\u001b[91m", "rgb": 16733525},
{"name": "light_purple", "code": "§d", "ansi": "\u001b[0m\u001b[95m", "rgb": 16733695},
{"name": "yellow", "code": "§e", "ansi": "\u001b[0m\u001b[93m", "rgb": 16777045},
{"name": "white", "code": "§f", "ansi": "\u001b[0m\u001b[97m", "rgb": 16777215}
],
"formatting": [
{"name": "bold", "code": "§l"},
{"name": "italic", "code": "§o"},
{"name": "underlined", "code": "§n"},
{"name": "strikethrough", "code": "§m"},
{"name": "obfuscated", "code": "§k"}
],
"reset": {"name": "reset", "code": "§r", "ansi": "\u001b[0m"}
}

View file

@ -0,0 +1,123 @@
const { colors } = require('./formatting.json')
const colorsByName = {}
const colorsByCode = {}
for (const color of colors) {
const hex = '#' + color.rgb.toString(16).padStart(6, '0')
colorsByName[color.name] = hex
colorsByCode[color.code] = hex
}
const reservedCharacters = {
'"': '&quot;',
"'": '&apos;',
'&': '&amp;',
'<': '&lt;',
'>': '&gt;'
}
function htmlStringify (text, { lang = {} } = {}) {
text = normalize(text)
let string = ''
if (text.text != null) string += preprocessText(text.text)
if (text.translate != null) {
let format
if (Object.hasOwn(lang, text.translate)) format = lang[text.translate]
else if (text.fallback != null) format = text.fallback
else format = text.translate
const _with = text.with || []
let i = 0
string += preprocessText(format).replace(/%(?:(\d+)\$)?(s|%)/g, (g0, g1) => {
if (g0 === '%%') return '%'
const idx = g1 ? parseInt(g1) : i++
if (_with[idx]) {
return htmlStringify(_with[idx], { lang })
}
return ''
})
}
if (text.selector != null) string += preprocessText(text.selector)
if (text.keybind) {
// TODO
}
if (text.extra) {
for (const extra of text.extra) {
string += htmlStringify(extra, { lang })
}
}
if (text.color) string = `<font color="${text.color[0] === '#' ? ('#' + text.color.substring(1).padStart(6, '0')) : colorsByName[text.color]}">${string}</font>`
// formatting
if (text.bold) string = `<b>${string}</b>`
if (text.italic) string = `<i>${string}</i>`
if (text.underlined) string = `<u>${string}</u>`
if (text.strikethrough) string = `<strike>${string}</strike>`
if (text.obfuscated) string = `<blink>${string}</blink>`
return string
}
function preprocessText (input) {
let string = ''
let closing = ''
for (let i = 0; i < input.length; i++) {
const c = input[i]
// Emulate Minecraft's section sign escape sequences
if (c === '§') {
if ((i + 1) >= input.length) break // For trailing section signs, append nothing
const code = input.substring(i, i + 2)
i++
const hex = colorsByCode[code]
if (hex) {
string += closing
string += `<font color="${hex}">`
closing = '</font>'
continue
}
if (code === '§r') {
string += closing
closing = ''
continue
}
if (code === '§l') { string += '<b>'; closing += '</b>' }
if (code === '§o') { string += '<i>'; closing += '</i>' }
if (code === '§n') { string += '<u>'; closing += '</u>' }
if (code === '§m') { string += '<strike>'; closing += '</strike>' }
if (code === '§k') { string += '<blink>'; closing += '</blink>' }
continue // Do not append the escape sequence itself to the string
}
else if (reservedCharacters[c]) string += reservedCharacters[c]
else if (c === '\n') string += '<br>'
else string += c
}
string += closing
return string
}
function normalize (text) {
if (typeof text === 'string') return { text }
if (Array.isArray(text)) {
const text2 = [...text]
const text3 = { ...normalize(text2.shift()) }
text3.extra = text2
return text3
}
return text
}
module.exports = htmlStringify

View file

@ -0,0 +1,12 @@
function parseJsonText (json) {
return JSON.parse(json)
}
function parseNbtText (data) {
if (typeof data.value !== 'object') return data.value
if (Array.isArray(data.value)) return [...data.value]
if (data.type === 'list') return data.value.value.map(value => parseNbtText({ value }))
return Object.fromEntries(Object.entries(data.value).map(([key, value]) => ([key === '' ? 'text' : key, parseNbtText(value)])))
}
module.exports = { parseJsonText, parseNbtText }

View file

@ -1,4 +1,4 @@
const parseText = require('./text_parser.js')
const parseText = require('../text_parser.js')
class TextMessage {
constructor (text) {

View file

@ -94,8 +94,6 @@ function parseJson (json, parent) {
if (json.obfuscated) raw += '§k'
if (json.text) {
raw += json.text
} else if (json['']) {
raw += json['']
}
if (json.translate) { // I checked with the native minecraft code. This is how Minecraft does the matching and group indexing. -hhhzzzsss
let format = language[json.translate]
@ -104,7 +102,7 @@ function parseJson (json, parent) {
const _with = json.with ?? []
let i = 0
raw += format.replace(/%(?:(\\d+)\\$)?(s|%)/g, (g0, g1) => {
raw += format.replace(/%(?:(\d+)\$)?(s|%)/g, (g0, g1) => {
if (g0 === '%%') {
return '%'
} else {