diff --git a/db/knex_migrations/2026-01-02-0713-gamedig-v4-to-v5.js b/db/knex_migrations/2026-01-02-0713-gamedig-v4-to-v5.js new file mode 100644 index 000000000..63931a705 --- /dev/null +++ b/db/knex_migrations/2026-01-02-0713-gamedig-v4-to-v5.js @@ -0,0 +1,198 @@ +// Migration to update monitor.game from GameDig v4 to v5 game IDs +// Reference: https://github.com/gamedig/node-gamedig/blob/master/MIGRATE_IDS.md + +// Lookup table mapping v4 game IDs to v5 game IDs +const gameDig4to5IdMap = { + "americasarmypg": "aapg", + "7d2d": "sdtd", + "as": "actionsource", + "ageofchivalry": "aoc", + "arkse": "ase", + "arcasimracing": "asr08", + "arma": "aaa", + "arma2oa": "a2oa", + "armacwa": "acwa", + "armar": "armaresistance", + "armare": "armareforger", + "armagetron": "armagetronadvanced", + "bat1944": "battalion1944", + "bf1942": "battlefield1942", + "bfv": "battlefieldvietnam", + "bf2": "battlefield2", + "bf2142": "battlefield2142", + "bfbc2": "bbc2", + "bf3": "battlefield3", + "bf4": "battlefield4", + "bfh": "battlefieldhardline", + "bd": "basedefense", + "bs": "bladesymphony", + "buildandshoot": "bas", + "cod4": "cod4mw", + "callofjuarez": "coj", + "chivalry": "cmw", + "commandos3": "c3db", + "cacrenegade": "cacr", + "contactjack": "contractjack", + "cs15": "counterstrike15", + "cs16": "counterstrike16", + "cs2": "counterstrike2", + "crossracing": "crce", + "darkesthour": "dhe4445", + "daysofwar": "dow", + "deadlydozenpt": "ddpt", + "dh2005": "deerhunter2005", + "dinodday": "ddd", + "dirttrackracing2": "dtr2", + "dmc": "deathmatchclassic", + "dnl": "dal", + "drakan": "dootf", + "dys": "dystopia", + "em": "empiresmod", + "empyrion": "egs", + "f12002": "formulaone2002", + "flashpointresistance": "ofr", + "fivem": "gta5f", + "forrest": "theforrest", + "graw": "tcgraw", + "graw2": "tcgraw2", + "giantscitizenkabuto": "gck", + "ges": "goldeneyesource", + "gore": "gus", + "hldm": "hld", + "hldms": "hlds", + "hlopfor": "hlof", + "hl2dm": "hl2d", + "hidden": "thehidden", + "had2": "hiddendangerous2", + "igi2": "i2cs", + "il2": "il2sturmovik", + "insurgencymic": "imic", + "isle": "theisle", + "jamesbondnightfire": "jb007n", + "jc2mp": "jc2m", + "jc3mp": "jc3m", + "kingpin": "kloc", + "kisspc": "kpctnc", + "kspdmp": "kspd", + "kzmod": "kreedzclimbing", + "left4dead": "l4d", + "left4dead2": "l4d2", + "m2mp": "m2m", + "mohsh": "mohaas", + "mohbt": "mohaab", + "mohab": "moha", + "moh2010": "moh", + "mohwf": "mohw", + "minecraftbe": "mbe", + "mtavc": "gtavcmta", + "mtasa": "gtasamta", + "ns": "naturalselection", + "ns2": "naturalselection2", + "nwn": "neverwinternights", + "nwn2": "neverwinternights2", + "nolf": "tonolf", + "nolf2": "nolf2asihw", + "pvkii": "pvak2", + "ps": "postscriptum", + "primalcarnage": "pce", + "pc": "projectcars", + "pc2": "projectcars2", + "prbf2": "prb2", + "przomboid": "projectzomboid", + "quake1": "quake", + "quake3": "q3a", + "ragdollkungfu": "rdkf", + "r6": "rainbowsix", + "r6roguespear": "rs2rs", + "r6ravenshield": "rs3rs", + "redorchestraost": "roo4145", + "redm": "rdr2r", + "riseofnations": "ron", + "rs2": "rs2v", + "samp": "gtasam", + "saomp": "gtasao", + "savage2": "s2ats", + "ss": "serioussam", + "ss2": "serioussam2", + "ship": "theship", + "sinep": "sinepisodes", + "sonsoftheforest": "sotf", + "swbf": "swb", + "swbf2": "swb2", + "swjk": "swjkja", + "swjk2": "swjk2jo", + "takeonhelicopters": "toh", + "tf2": "teamfortress2", + "terraria": "terrariatshock", + "tribes1": "t1s", + "ut": "unrealtournament", + "ut2003": "unrealtournament2003", + "ut2004": "unrealtournament2004", + "ut3": "unrealtournament3", + "v8supercar": "v8sc", + "vcmp": "vcm", + "vs": "vampireslayer", + "wheeloftime": "wot", + "wolfenstein2009": "wolfenstein", + "wolfensteinet": "wet", + "wurm": "wurmunlimited", +}; + +/** + * Migrate game IDs from v4 to v5 + * @param {import("knex").Knex} knex - Knex instance + * @returns {Promise} + */ +exports.up = async function (knex) { + await knex.transaction(async (trx) => { + // Get all monitors that use the gamedig type + const monitors = await trx("monitor") + .select("id", "game") + .where("type", "gamedig") + .whereNotNull("game"); + + // Update each monitor with the new game ID if it needs migration + for (const monitor of monitors) { + const oldGameId = monitor.game; + const newGameId = gameDig4to5IdMap[oldGameId]; + + if (newGameId) { + await trx("monitor") + .where("id", monitor.id) + .update({ game: newGameId }); + } + } + }); +}; + +/** + * Revert game IDs from v5 back to v4 + * @param {import("knex").Knex} knex - Knex instance + * @returns {Promise} + */ +exports.down = async function (knex) { + // Create reverse mapping from the same LUT + const gameDig5to4IdMap = Object.fromEntries( + Object.entries(gameDig4to5IdMap).map(([ v4, v5 ]) => [ v5, v4 ]) + ); + + await knex.transaction(async (trx) => { + // Get all monitors that use the gamedig type + const monitors = await trx("monitor") + .select("id", "game") + .where("type", "gamedig") + .whereNotNull("game"); + + // Revert each monitor back to the old game ID if it was migrated + for (const monitor of monitors) { + const newGameId = monitor.game; + const oldGameId = gameDig5to4IdMap[newGameId]; + + if (oldGameId) { + await trx("monitor") + .where("id", monitor.id) + .update({ game: oldGameId }); + } + } + }); +}; diff --git a/package-lock.json b/package-lock.json index ab56e410d..12ace22aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "express-static-gzip": "~2.1.7", "feed": "^4.2.2", "form-data": "~4.0.0", - "gamedig": "^4.2.0", + "gamedig": "^5.0.1", "html-escaper": "^3.0.3", "http-cookie-agent": "~5.0.4", "http-graceful-shutdown": "~3.1.7", @@ -4956,12 +4956,12 @@ "license": "BSD-3-Clause" }, "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sindresorhus/is?sponsor=1" @@ -5760,18 +5760,6 @@ "@popperjs/core": "^2.9.2" } }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -5893,15 +5881,6 @@ "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==", "license": "MIT" }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/koa": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.15.0.tgz", @@ -5984,15 +5963,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", @@ -7820,54 +7790,30 @@ } }, "node_modules/cacheable-lookup": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", - "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "license": "MIT", "engines": { - "node": ">=10.6.0" + "node": ">=14.16" } }, "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "license": "MIT", "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "license": "MIT", - "engines": { - "node": ">=8" + "node": ">=14.16" } }, "node_modules/call-bind": { @@ -8200,18 +8146,6 @@ "node": ">=12" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -9543,6 +9477,14 @@ "dev": true, "license": "ISC" }, + "node_modules/emitter-component": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", + "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -10365,7 +10307,6 @@ } ], "license": "MIT", - "optional": true, "dependencies": { "strnum": "^2.1.0" }, @@ -10686,10 +10627,13 @@ } }, "node_modules/form-data-encoder": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", - "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==", - "license": "MIT" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "license": "MIT", + "engines": { + "node": ">= 14.17" + } }, "node_modules/formdata-node": { "version": "6.0.3", @@ -10894,171 +10838,42 @@ } }, "node_modules/gamedig": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/gamedig/-/gamedig-4.3.2.tgz", - "integrity": "sha512-TjYwybvy8HNAhkv2EJccd5HROIiMeMriWmeX8vT8m5Ibat5JMzVpugzsD8L8XZVrOfiXnVg/9DhWYM8k/VG/vw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/gamedig/-/gamedig-5.3.2.tgz", + "integrity": "sha512-R2b1LwjW783PZsHRl9M8R06UkvJwXmJ6PDKk48UukJQ9ktiwrCeAk90MAZx6nF3oA444uf7r5eHjfaYbNoSV+Q==", "license": "MIT", "dependencies": { - "cheerio": "1.0.0-rc.10", + "fast-xml-parser": "5.2.5", "gbxremote": "0.2.1", - "got": "12.1.0", - "iconv-lite": "0.6.3", - "long": "5.2.0", - "minimist": "1.2.6", - "punycode": "2.1.1", + "got": "13.0.0", + "iconv-lite": "0.7.0", + "long": "5.3.2", + "minimist": "1.2.8", "seek-bzip": "2.0.0", + "telnet-client": "2.2.6", "varint": "6.0.0" }, "bin": { "gamedig": "bin/gamedig.js" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.20.0" } }, - "node_modules/gamedig/node_modules/cheerio": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", - "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", + "node_modules/gamedig/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", "license": "MIT", "dependencies": { - "cheerio-select": "^1.5.0", - "dom-serializer": "^1.3.2", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" }, "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/gamedig/node_modules/cheerio-select": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.6.0.tgz", - "integrity": "sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==", - "license": "BSD-2-Clause", - "dependencies": { - "css-select": "^4.3.0", - "css-what": "^6.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.3.1", - "domutils": "^2.8.0" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/gamedig/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/gamedig/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/gamedig/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/gamedig/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/gamedig/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/gamedig/node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/gamedig/node_modules/long": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", - "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==", - "license": "Apache-2.0" - }, - "node_modules/gamedig/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "license": "MIT" - }, - "node_modules/gamedig/node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "license": "MIT", - "dependencies": { - "parse5": "^6.0.1" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/gauge": { @@ -11415,27 +11230,25 @@ } }, "node_modules/got": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", - "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", + "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", "license": "MIT", "dependencies": { - "@sindresorhus/is": "^4.6.0", + "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", - "@types/cacheable-request": "^6.0.2", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^6.0.4", - "cacheable-request": "^7.0.2", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", "decompress-response": "^6.0.0", - "form-data-encoder": "1.7.1", + "form-data-encoder": "^2.1.2", "get-stream": "^6.0.1", "http2-wrapper": "^2.1.10", "lowercase-keys": "^3.0.0", "p-cancelable": "^3.0.0", - "responselike": "^2.0.0" + "responselike": "^3.0.0" }, "engines": { - "node": ">=14.16" + "node": ">=16" }, "funding": { "url": "https://github.com/sindresorhus/got?sponsor=1" @@ -13623,12 +13436,15 @@ } }, "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "license": "MIT", "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimalistic-assert": { @@ -13653,10 +13469,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "license": "MIT" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minimist-options": { "version": "4.1.0", @@ -14107,6 +13926,12 @@ "node": ">= 0.6" } }, + "node_modules/net": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz", + "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==", + "license": "MIT" + }, "node_modules/net-snmp": { "version": "3.26.0", "resolved": "https://registry.npmjs.org/net-snmp/-/net-snmp-3.26.0.tgz", @@ -14388,12 +14213,12 @@ } }, "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -16272,26 +16097,20 @@ } }, "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "license": "MIT", "dependencies": { - "lowercase-keys": "^2.0.0" + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/responselike/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/retimer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/retimer/-/retimer-3.0.0.tgz", @@ -17254,6 +17073,15 @@ "node": ">= 0.4" } }, + "node_modules/stream": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", + "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", + "license": "MIT", + "dependencies": { + "emitter-component": "^1.1.1" + } + }, "node_modules/stream-shift": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", @@ -17552,8 +17380,7 @@ "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/style-search": { "version": "0.1.0", @@ -17969,6 +17796,20 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/telnet-client": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/telnet-client/-/telnet-client-2.2.6.tgz", + "integrity": "sha512-ZUYrLsPtQupQww3eSEORDVOb6ztdtKEghya6TVXPo2tg/UQq2pn5rHhvwuUvyYpbnsoqdNY1fyD1GNkXHR8dYA==", + "license": "MIT", + "dependencies": { + "net": "^1.0.2", + "stream": "^0.0.2" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + } + }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -19130,16 +18971,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/wait-on/node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/web-push": { "version": "3.6.7", "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz", diff --git a/package.json b/package.json index 987b5af52..09811f1ad 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "express-static-gzip": "~2.1.7", "feed": "^4.2.2", "form-data": "~4.0.0", - "gamedig": "^4.2.0", + "gamedig": "^5.0.1", "html-escaper": "^3.0.3", "http-cookie-agent": "~5.0.4", "http-graceful-shutdown": "~3.1.7", diff --git a/server/monitor-types/gamedig.js b/server/monitor-types/gamedig.js index b20c2435c..35a015f98 100644 --- a/server/monitor-types/gamedig.js +++ b/server/monitor-types/gamedig.js @@ -1,6 +1,6 @@ const { MonitorType } = require("./monitor-type"); -const { UP, DOWN } = require("../../src/util"); -const Gamedig = require("gamedig"); +const { UP } = require("../../src/util"); +const { GameDig } = require("gamedig"); const dns = require("dns").promises; const net = require("net"); @@ -11,15 +11,13 @@ class GameDigMonitorType extends MonitorType { * @inheritdoc */ async check(monitor, heartbeat, server) { - heartbeat.status = DOWN; - let host = monitor.hostname; if (net.isIP(host) === 0) { host = await this.resolveHostname(host); } try { - const state = await Gamedig.query({ + const state = await GameDig.query({ type: monitor.game, host: host, port: monitor.port, diff --git a/server/socket-handlers/general-socket-handler.js b/server/socket-handlers/general-socket-handler.js index b996efe7b..ad57d7b1e 100644 --- a/server/socket-handlers/general-socket-handler.js +++ b/server/socket-handlers/general-socket-handler.js @@ -2,30 +2,35 @@ const { log } = require("../../src/util"); const { Settings } = require("../settings"); const { sendInfo } = require("../client"); const { checkLogin } = require("../util-server"); -const GameResolver = require("gamedig/lib/GameResolver"); +const { games } = require("gamedig"); const { testChrome } = require("../monitor-types/real-browser-monitor-type"); const fsAsync = require("fs").promises; const path = require("path"); -let gameResolver = new GameResolver(); -let gameList = null; - /** * Get a game list via GameDig - * @returns {object[]} list of games supported by GameDig + * @returns {object} list of games supported by GameDig */ function getGameList() { - if (gameList == null) { - gameList = gameResolver._readGames().games.sort((a, b) => { - if ( a.pretty < b.pretty ) { - return -1; - } - if ( a.pretty > b.pretty ) { - return 1; - } - return 0; - }); - } + let gameList = []; + gameList = Object.keys(games).map(key => { + const item = games[key]; + return { + keys: [ key ], + pretty: item.name, + options: item.options, + extra: item.extra || {} + }; + }); + gameList.sort((a, b) => { + if ( a.pretty < b.pretty ) { + return -1; + } + if ( a.pretty > b.pretty ) { + return 1; + } + return 0; + }); return gameList; } diff --git a/test/backend-test/monitors/test-gamedig.js b/test/backend-test/monitors/test-gamedig.js index 57e7a89dc..5274e69dc 100644 --- a/test/backend-test/monitors/test-gamedig.js +++ b/test/backend-test/monitors/test-gamedig.js @@ -1,15 +1,15 @@ const { describe, test, mock } = require("node:test"); const assert = require("node:assert"); const { GameDigMonitorType } = require("../../../server/monitor-types/gamedig"); -const { UP, DOWN, PENDING } = require("../../../src/util"); +const { UP, PENDING } = require("../../../src/util"); const net = require("net"); -const Gamedig = require("gamedig"); +const { GameDig } = require("gamedig"); describe("GameDig Monitor", () => { test("check() sets status to UP when Gamedig.query returns valid server response", async () => { const gamedigMonitor = new GameDigMonitorType(); - mock.method(Gamedig, "query", async () => { + mock.method(GameDig, "query", async () => { return { name: "Test Minecraft Server", ping: 42, @@ -43,7 +43,7 @@ describe("GameDig Monitor", () => { test("check() resolves hostname to IP address when hostname is not an IP", async () => { const gamedigMonitor = new GameDigMonitorType(); - mock.method(Gamedig, "query", async (options) => { + mock.method(GameDig, "query", async (options) => { assert.ok( net.isIP(options.host) !== 0, `Expected IP address, got ${options.host}` @@ -82,7 +82,7 @@ describe("GameDig Monitor", () => { let capturedOptions = null; - mock.method(Gamedig, "query", async (options) => { + mock.method(GameDig, "query", async (options) => { capturedOptions = options; return { name: "Test Server", @@ -117,7 +117,7 @@ describe("GameDig Monitor", () => { let capturedOptions = null; - mock.method(Gamedig, "query", async (options) => { + mock.method(GameDig, "query", async (options) => { capturedOptions = options; return { name: "Test Server", @@ -152,7 +152,7 @@ describe("GameDig Monitor", () => { let capturedOptions = null; - mock.method(Gamedig, "query", async (options) => { + mock.method(GameDig, "query", async (options) => { capturedOptions = options; return { name: "Test Server", @@ -189,7 +189,7 @@ describe("GameDig Monitor", () => { let capturedOptions = null; - mock.method(Gamedig, "query", async (options) => { + mock.method(GameDig, "query", async (options) => { capturedOptions = options; return { name: "Test Server", @@ -219,7 +219,7 @@ describe("GameDig Monitor", () => { } }); - test("check() sets status to DOWN and rejects when game server is unreachable", async () => { + test("check() rejects when game server is unreachable", async () => { const gamedigMonitor = new GameDigMonitorType(); const monitor = { @@ -238,8 +238,6 @@ describe("GameDig Monitor", () => { gamedigMonitor.check(monitor, heartbeat, {}), /Error/ ); - - assert.strictEqual(heartbeat.status, DOWN); }); test("resolveHostname() returns IP address when given valid hostname", async () => {