hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cache/mod_versions.json8
-rw-r--r--eslint.config.js53
-rw-r--r--package-lock.json324
-rw-r--r--package.json8
-rw-r--r--src/app.js192
5 files changed, 526 insertions, 59 deletions
diff --git a/cache/mod_versions.json b/cache/mod_versions.json
new file mode 100644
index 0000000..e371579
--- /dev/null
+++ b/cache/mod_versions.json
@@ -0,0 +1,8 @@
+{
+ "audioplayer": "lr4WB1az",
+ "convenient-mobgriefing": "wSzzf9N6",
+ "image2map": "1dazBIRA",
+ "lithium": "oGKQMdyZ",
+ "player-roles": "Qi5eDlej",
+ "distanthorizons": "9N4UxA0o"
+} \ No newline at end of file
diff --git a/eslint.config.js b/eslint.config.js
deleted file mode 100644
index adf7d1b..0000000
--- a/eslint.config.js
+++ /dev/null
@@ -1,53 +0,0 @@
-const js = require('@eslint/js');
-
-module.exports = [
- js.configs.recommended,
- {
- languageOptions: {
- ecmaVersion: 'latest',
- },
- rules: {
- 'arrow-spacing': ['warn', { before: true, after: true }],
- 'brace-style': ['error', 'allman', { allowSingleLine: true }],
- 'comma-dangle': ['error', 'always-multiline'],
- 'comma-spacing': 'error',
- 'comma-style': 'error',
- curly: ['error', 'multi-line', 'consistent'],
- 'dot-location': ['error', 'property'],
- 'handle-callback-err': 'off',
- indent: ['error', 'tab'],
- 'keyword-spacing': 'error',
- 'max-nested-callbacks': ['error', { max: 4 }],
- 'max-statements-per-line': ['error', { max: 2 }],
- 'no-console': 'off',
- 'no-empty-function': 'error',
- 'no-floating-decimal': 'error',
- 'no-inline-comments': 'error',
- 'no-lonely-if': 'error',
- 'no-multi-spaces': 'error',
- 'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1, maxBOF: 0 }],
- 'no-shadow': ['error', { allow: ['err', 'resolve', 'reject'] }],
- 'no-trailing-spaces': ['error'],
- 'no-var': 'error',
- 'no-undef': 'off',
- 'object-curly-spacing': ['error', 'always'],
- 'prefer-const': 'error',
- quotes: ['error', 'single'],
- semi: ['error', 'always'],
- 'space-before-blocks': 'error',
- 'space-before-function-paren': [
- 'error',
- {
- anonymous: 'never',
- named: 'never',
- asyncArrow: 'always',
- },
- ],
- 'space-in-parens': 'error',
- 'space-infix-ops': 'error',
- 'space-unary-ops': 'error',
- 'spaced-comment': 'error',
- yoda: 'error',
- },
- },
-]; \ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 8b8064f..b66ca89 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,9 @@
"version": "1.0.0",
"license": "Apache-2.0",
"dependencies": {
- "discord.js": "^14.23.2"
+ "axios": "^1.12.2",
+ "discord.js": "^14.23.2",
+ "fs-extra": "^11.3.2"
},
"devDependencies": {
"@eslint/js": "^9.37.0",
@@ -477,6 +479,23 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/axios": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
+ "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.4",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -495,6 +514,19 @@
"concat-map": "0.0.1"
}
},
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -542,6 +574,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -589,6 +633,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/discord-api-types": {
"version": "0.38.29",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.29.tgz",
@@ -625,6 +678,65 @@
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -864,6 +976,102 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "11.3.2",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz",
+ "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==",
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -890,6 +1098,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "license": "ISC"
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -900,6 +1126,45 @@
"node": ">=8"
}
},
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -1001,6 +1266,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -1066,6 +1343,36 @@
"integrity": "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA==",
"license": "MIT"
},
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -1186,6 +1493,12 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -1295,6 +1608,15 @@
"integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==",
"license": "MIT"
},
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
diff --git a/package.json b/package.json
index e7b73bc..4b64e2a 100644
--- a/package.json
+++ b/package.json
@@ -18,10 +18,8 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
- "discord.js": "^14.23.2"
- },
- "devDependencies": {
- "@eslint/js": "^9.37.0",
- "eslint": "^9.37.0"
+ "axios": "^1.12.2",
+ "discord.js": "^14.23.2",
+ "fs-extra": "^11.3.2"
}
}
diff --git a/src/app.js b/src/app.js
index e69de29..2a67f0b 100644
--- a/src/app.js
+++ b/src/app.js
@@ -0,0 +1,192 @@
+const { Client, Events, GatewayIntentBits } = require("discord.js")
+const axios = require("axios")
+const fs = require("fs-extra")
+const path = require("path")
+const config = require("../config.json")
+
+const {
+ token,
+ channel_id,
+ minecraft_version,
+ modrinth_mods,
+} = config
+
+const cacheFile = path.join(__dirname, "../cache/mod_versions.json")
+
+const client = new Client({ intents: [GatewayIntentBits.Guilds] })
+const strictVersionMatch = true
+
+const LoadCache = async () =>
+{
+ try
+ {
+ if (await fs.pathExists(cacheFile))
+ return JSON.parse(await fs.readFile(cacheFile, "utf8"))
+ }
+ catch (err)
+ {
+ console.error("[Cache] Failed to read cache:", err)
+ }
+ return {}
+}
+
+const SaveCache = async (cache) =>
+{
+ try
+ {
+ await fs.ensureDir(path.dirname(cacheFile))
+ await fs.writeFile(cacheFile, JSON.stringify(cache, null, 2))
+ }
+ catch (err)
+ {
+ console.error("[Cache] Failed to save cache:", err)
+ }
+}
+
+const GetLatestFabricVersion = async (slug) =>
+{
+ try
+ {
+ const res = await axios.get(`https://api.modrinth.com/v2/project/${slug}/version`)
+ const versions = res.data.filter(v => v.loaders.includes("fabric"))
+ if (versions.length === 0) return null
+
+ return versions.sort(
+ (a, b) => new Date(b.date_published) - new Date(a.date_published)
+ )[0]
+ }
+ catch (err)
+ {
+ console.error(`[Modrinth] Error fetching ${slug}:`, err.message)
+ return null
+ }
+}
+
+const IsVersionCompatible = (modVersionList, targetVersion) =>
+{
+ if (strictVersionMatch)
+ return modVersionList.includes(targetVersion)
+ else
+ return modVersionList.some(v => targetVersion.startsWith(v))
+}
+
+const CheckModUpdates = async () =>
+{
+ console.log("[Check] Checking for mod updates...")
+ const cache = await LoadCache()
+ const newCache = { ...cache }
+ const updates = []
+ const upToDate = []
+ const waitingUpdate = []
+
+ for (const slug of modrinth_mods)
+ {
+ const latest = await GetLatestFabricVersion(slug)
+ if (!latest) continue
+
+ const compatible = IsVersionCompatible(latest.game_versions, minecraft_version)
+ const latestId = latest.id
+ const cachedId = cache[slug]
+
+ if (compatible)
+ {
+ if (cachedId !== latestId)
+ {
+ newCache[slug] = latestId
+ updates.push({
+ slug,
+ version: latest.version_number,
+ url: `https://modrinth.com/mod/${slug}/version/${latestId}`,
+ })
+ }
+ else
+ {
+ upToDate.push({
+ slug,
+ version: latest.version_number,
+ url: `https://modrinth.com/mod/${slug}/version/${latestId}`,
+ })
+ console.log(`[Check] ${slug} is up to date.`)
+ }
+ }
+ else
+ {
+ waitingUpdate.push({
+ slug,
+ version: latest.version_number,
+ url: `https://modrinth.com/mod/${slug}/version/${latestId}`,
+ })
+ console.log(`[Check] ${slug} waiting for update — not compatible with ${minecraft_version}`)
+ }
+ }
+
+ await SaveCache(newCache)
+ return { updates, upToDate, waitingUpdate }
+}
+
+const SendUpdateMessages = async (channel, updates, upToDate, waitingUpdate) =>
+{
+ if (updates.length === 0)
+ {
+ console.log("[Update]: No new updates.");
+ return;
+ }
+
+ let message = ""
+ message += `**Mod Update Check — Minecraft ${minecraft_version}**\n\n`
+
+ if (updates.length > 0)
+ {
+ message += `**🟢 Updated Mods:**\n`
+ for (const { slug, url } of updates)
+ message += `✅ [${slug}](${url}) updated\n`
+ message += `\n`
+ }
+
+ if (upToDate.lengt > 0)
+ {
+ message += `**⚪ Up-to-Date Mods:**\n`
+ for (const { slug, url } of upToDate)
+ message += `✅ [${slug}](${url})\n`
+ message += `\n`
+ }
+
+ if (waitingUpdate.length > 0)
+ {
+ message += `**⏳ Mods Waiting for Update:**\n`
+ for (const { slug, url } of waitingUpdate)
+ message += `❌ [${slug}](${url})\n`
+ }
+
+ await channel.send(
+{
+ content: message,
+ allowedMentions: { parse: [] },
+ flags: 4
+ })
+ console.log(`[Discord] Sent update summary (${updates.length} updated, ${upToDate.length} up-to-date, ${waitingUpdate.length} waiting).`)
+}
+
+client.once(Events.ClientReady, async readyClient =>
+{
+ console.log(`[OK] Logged in as ${readyClient.user.tag}`)
+
+ const channel = await client.channels.fetch(channel_id).catch(() => null)
+ if (!channel)
+ {
+ console.error("❌ Could not find channel ID:", channel_id)
+ return
+ }
+
+ const { updates, upToDate, waitingUpdate } = await CheckModUpdates()
+ await SendUpdateMessages(channel, updates, upToDate, waitingUpdate)
+
+ // ⏰ Check every hour (3600000 ms)
+ setInterval(async () =>
+ {
+ const { updates, upToDate, waitingUpdate } = await CheckModUpdates()
+ await SendUpdateMessages(channel, updates, upToDate, waitingUpdate)
+ }, 60 * 60 * 1000)
+})
+
+client.login(token)