diff options
| author | hachem <im@hachem.wtf> | 2025-12-09 23:02:55 +0100 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2025-12-09 23:02:55 +0100 |
| commit | 5cc4c5a689e124e3951c80f0cfda0de4a797fec7 (patch) | |
| tree | 7cb412c67f54636a4926161f4c948b68d87e74e8 | |
| parent | 50e62e246fef7975866c244e08ead7c681d50349 (diff) | |
[add]: merging and refactor
| -rw-r--r-- | README.md | 25 | ||||
| -rw-r--r-- | cli/app.js | 138 | ||||
| -rw-r--r-- | cli/commands/check.js | 81 | ||||
| -rw-r--r-- | cli/commands/clean.js | 80 | ||||
| -rw-r--r-- | cli/commands/generate.js (renamed from updater/cli.js) | 168 | ||||
| -rw-r--r-- | cli/commands/list.js | 15 | ||||
| -rw-r--r-- | cli/commands/merge.js | 156 | ||||
| -rw-r--r-- | config/config.json | 24 | ||||
| -rw-r--r-- | package.json | 5 |
9 files changed, 505 insertions, 187 deletions
@@ -18,6 +18,9 @@ npm install "minecraft_version": "1.21.10", "fabric_loader_version": "0.17.3", "loader_meta_version": "1.1.0", + "java_path": null, + "server_location": "C:/path/to/your/server", + "backup_location": "C:/path/to/backups", "modrinth_mods": [ "fabric-api", "lithium", @@ -53,10 +56,10 @@ npm run benson ``` ╔══════════════════════════════════════╗ ║ BENSON CLI v1.0 ║ - ║ Fabric Mod Manager for MC 1.21.10 ║ + ║ Fabric Server for MC 1.21.10 ║ ╚══════════════════════════════════════╝ - Type 'help' for available commands. + Type 'help' for commands. benson> _ ``` @@ -66,10 +69,12 @@ benson> _ | Command | Description | |---------|-------------| | `list-mods` | List configured mods | +| `check` | Check if all mods are compatible before generating | | `generate` | Download mods + server, run to generate files | -| `merge` | (coming soon) | -| `clean generated` | Remove generated server files | -| `clean all` | (coming soon) | +| `merge` | Backup server, move generated files to server location | +| `clean generated` | Remove server directory | +| `clean latest` | Remove latest backup zip | +| `clean all` | Remove both generated and latest backup | | `help` | Show available commands | | `exit` | Exit the CLI | @@ -82,8 +87,14 @@ Benson/ │ └── config.js # Shared config/cache utilities ├── bot/ │ └── app.js # Discord bot -├── updater/ -│ └── cli.js # Interactive CLI +├── cli/ +│ ├── app.js # CLI entry point +│ └── commands/ +│ ├── check.js # Check command +│ ├── clean.js # Clean commands +│ ├── generate.js # Generate command +│ ├── list.js # List-mods command +│ └── merge.js # Merge command ├── config/ │ ├── config.json # Main configuration │ └── api.json # Discord token (not in repo) diff --git a/cli/app.js b/cli/app.js new file mode 100644 index 0000000..1252358 --- /dev/null +++ b/cli/app.js @@ -0,0 +1,138 @@ +#!/usr/bin/env node + +const readline = require("readline") + +const { LoadConfig } = require("../lib/config") + +const { Check } = require("./commands/check") +const { Generate } = require("./commands/generate") +const { Merge } = require("./commands/merge") +const { CleanGenerated, CleanLatest, CleanAll } = require("./commands/clean") +const { ListMods } = require("./commands/list") + +const pkg = require("../package.json") +const config = LoadConfig() +const { minecraft_version } = config + +const ShowHelp = () => +{ + console.log(` + Commands: + + list-mods List configured mods + check Check if all mods are compatible before generating + generate Download mods + server, run to generate files + merge Backup server, move generated files to server location + clean generated Remove server directory + clean latest Remove latest backup zip + clean all Remove both generated and latest backup + help Show this help + exit Exit +`) +} + +const ProcessCommand = async (input, rl) => +{ + const parts = input.trim().toLowerCase().split(/\s+/) + const cmd = parts[0] + const arg = parts[1] + + switch (cmd) + { + case "list-mods": + ListMods() + break + + case "check": + await Check() + break + + case "generate": + await Generate(rl) + break + + case "merge": + await Merge() + break + + case "clean": + if (arg === "generated") + await CleanGenerated() + else if (arg === "latest") + await CleanLatest() + else if (arg === "all") + await CleanAll() + else + console.log(" Usage: clean generated | clean latest | clean all") + break + + case "help": + case "?": + ShowHelp() + break + + case "exit": + case "quit": + case "q": + console.log(" Goodbye!\n") + process.exit(0) + + case "": + break + + default: + console.log(` Unknown command: ${cmd}. Type 'help' for commands.`) + } +} + +const DrawBox = (lines) => +{ + const maxLen = Math.max(...lines.map(l => l.length)) + const pad = (str) => str + " ".repeat(maxLen - str.length) + + const top = "╔" + "═".repeat(maxLen + 2) + "╗" + const bot = "╚" + "═".repeat(maxLen + 2) + "╝" + const mid = lines.map(l => "║ " + pad(l) + " ║").join("\n ") + + return ` ${top}\n ${mid}\n ${bot}` +} + +const Main = () => +{ + const title = `BENSON CLI v${pkg.version}` + const subtitle = `Fabric Server for MC ${minecraft_version}` + + const box = DrawBox([title, subtitle]) + + console.log(` +${box} + + Type 'help' for commands. +`) + + const rl = readline.createInterface + ({ + input: process.stdin, + output: process.stdout + }) + + const Prompt = () => + { + rl.question("benson> ", async (input) => + { + await ProcessCommand(input, rl) + Prompt() + }) + } + + rl.on("close", () => + { + console.log("\n Goodbye!\n") + process.exit(0) + }) + + Prompt() +} + +Main() + diff --git a/cli/commands/check.js b/cli/commands/check.js new file mode 100644 index 0000000..f91b710 --- /dev/null +++ b/cli/commands/check.js @@ -0,0 +1,81 @@ +const { LoadConfig } = require("../../lib/config") +const { GetModVersions, GetCompatibleVersion } = require("../../lib/modrinth") + +const config = LoadConfig() +const { minecraft_version, modrinth_mods } = config + +const Check = async () => +{ + console.log(`\n Checking mod compatibility for MC ${minecraft_version} (Fabric)...\n`) + + const compatible = [] + const incompatible = [] + const failed = [] + + for (const slug of modrinth_mods) + { + try + { + const versions = await GetModVersions(slug) + if (!versions) + { + failed.push(slug) + continue + } + + const match = GetCompatibleVersion(versions, minecraft_version, "fabric") + if (match) + { + const filename = match.files[0]?.filename || match.version_number + compatible.push({ slug, version: filename }) + } + else + { + incompatible.push(slug) + } + } + catch (err) + { + failed.push(slug) + } + } + + // Display results + if (compatible.length) + { + console.log(" [OK] Compatible:") + for (const { slug, version } of compatible) + console.log(` ${slug} -> ${version}`) + console.log("") + } + + if (incompatible.length) + { + console.log(" [FAIL] No compatible version:") + for (const slug of incompatible) + console.log(` ${slug}`) + console.log("") + } + + if (failed.length) + { + console.log(" [WARN] Failed to check:") + for (const slug of failed) + console.log(` ${slug}`) + console.log("") + } + + // Summary + const total = modrinth_mods.length + const ok = compatible.length + + console.log(` Summary: ${ok}/${total} mods compatible\n`) + + if (incompatible.length === 0 && failed.length === 0) + console.log(" [OK] Safe to generate!\n") + else + console.log(" [FAIL] Not all mods are compatible. Generate may have issues.\n") +} + +module.exports = { Check } + diff --git a/cli/commands/clean.js b/cli/commands/clean.js new file mode 100644 index 0000000..99a96f6 --- /dev/null +++ b/cli/commands/clean.js @@ -0,0 +1,80 @@ +const fs = require("fs-extra") +const path = require("path") + +const { LoadConfig } = require("../../lib/config") +const { serverDir } = require("./generate") + +const config = LoadConfig() +const { backup_location } = config + +const CleanGenerated = async () => +{ + try + { + if (await fs.pathExists(serverDir)) + { + await fs.remove(serverDir) + console.log(" Removed server directory.") + } + else + { + console.log(" Server directory doesn't exist.") + } + } + catch (err) + { + console.error(" Failed to remove server directory:", err.message) + } +} + +const CleanLatest = async () => +{ + if (!backup_location) + { + console.log(" Error: backup_location must be set in config.") + return + } + + if (!await fs.pathExists(backup_location)) + { + console.log(" Backup directory doesn't exist.") + return + } + + try + { + const files = await fs.readdir(backup_location) + const zipFiles = files + .filter(f => f.startsWith("backup_") && f.endsWith(".zip")) + .sort() + .reverse() + + if (zipFiles.length === 0) + { + console.log(" No backup files found.") + return + } + + const latestZip = zipFiles[0] + const latestPath = path.join(backup_location, latestZip) + + await fs.remove(latestPath) + console.log(` Removed latest backup: ${latestZip}`) + } + catch (err) + { + console.error(" Failed to remove latest backup:", err.message) + } +} + +const CleanAll = async () => +{ + console.log(" Cleaning generated files...") + await CleanGenerated() + console.log("") + console.log(" Cleaning latest backup...") + await CleanLatest() +} + +module.exports = { CleanGenerated, CleanLatest, CleanAll } + diff --git a/updater/cli.js b/cli/commands/generate.js index 8731ab6..183810d 100644 --- a/updater/cli.js +++ b/cli/commands/generate.js @@ -1,14 +1,10 @@ -#!/usr/bin/env node - -const readline = require("readline") const fs = require("fs-extra") const path = require("path") const { spawn } = require("child_process") -const { LoadConfig } = require("../lib/config") -const { GetModVersions, GetCompatibleVersion, DownloadFile } = require("../lib/modrinth") +const { LoadConfig } = require("../../lib/config") +const { GetModVersions, GetCompatibleVersion, DownloadFile } = require("../../lib/modrinth") -const pkg = require("../package.json") const config = LoadConfig() const @@ -22,7 +18,7 @@ const const javaCmd = java_path || "java" -const serverDir = path.join(__dirname, "../server") +const serverDir = path.join(__dirname, "../../server") const modsDir = path.join(serverDir, "mods") const deleteList = @@ -41,7 +37,6 @@ const deleteList = "eula.txt" ] - const EnsureDirs = async () => { await fs.ensureDir(serverDir) @@ -215,40 +210,6 @@ const DeletePostGenerate = async () => console.log(` Removed ${removed} items.`) } -const CleanGenerated = async () => -{ - try - { - if (await fs.pathExists(serverDir)) - { - await fs.remove(serverDir) - console.log(" Removed server directory.") - } - else - { - console.log(" Server directory doesn't exist.") - } - } - catch (err) - { - console.error(" Failed to remove server directory:", err.message) - } -} - -const CleanAll = async () => -{ - // Placeholder - will be implemented later - console.log(" clean all - not implemented yet") -} - -const ListMods = () => -{ - console.log(`\n Mods for MC ${minecraft_version}:\n`) - for (const slug of modrinth_mods) - console.log(` - ${slug}`) - console.log(`\n Total: ${modrinth_mods.length} mods\n`) -} - const Generate = async (rl) => { await EnsureDirs() @@ -256,9 +217,9 @@ const Generate = async (rl) => console.log(`\n Generating server for MC ${minecraft_version}...\n`) const { downloadedFiles, failedMods } = await DownloadModrinthMods(modrinth_mods, minecraft_version) - console.log(`\n ✅ Downloaded ${downloadedFiles.length} mods`) + console.log(`\n [OK] Downloaded ${downloadedFiles.length} mods`) if (failedMods.length) - console.log(` ❌ Failed: ${failedMods.join(", ")}`) + console.log(` [FAIL] Failed: ${failedMods.join(", ")}`) const serverFile = await DownloadFabricServer(minecraft_version, fabric_loader_version, loader_meta_version) @@ -272,122 +233,5 @@ const Generate = async (rl) => console.log("") } -const Merge = async () => -{ - // Placeholder - will be implemented later - console.log(" merge - not implemented yet") -} - -const ShowHelp = () => -{ - console.log(` - Commands: - - list-mods List configured mods - generate Download mods + server, run to generate files - merge (coming soon) - clean generated Remove generated server files - clean all (coming soon) - help Show this help - exit Exit -`) -} - -const ProcessCommand = async (input, rl) => -{ - const parts = input.trim().toLowerCase().split(/\s+/) - const cmd = parts[0] - const arg = parts[1] - - switch (cmd) - { - case "list-mods": - ListMods() - break - - case "generate": - await Generate(rl) - break - - case "merge": - await Merge() - break - - case "clean": - if (arg === "generated") - await CleanGenerated() - else if (arg === "all") - await CleanAll() - else - console.log(" Usage: clean generated | clean all") - break - - case "help": - case "?": - ShowHelp() - break - - case "exit": - case "quit": - case "q": - console.log(" Goodbye!\n") - process.exit(0) - - case "": - break - - default: - console.log(` Unknown command: ${cmd}. Type 'help' for commands.`) - } -} - -const DrawBox = (lines) => -{ - const maxLen = Math.max(...lines.map(l => l.length)) - const pad = (str) => str + " ".repeat(maxLen - str.length) - - const top = "╔" + "═".repeat(maxLen + 2) + "╗" - const bot = "╚" + "═".repeat(maxLen + 2) + "╝" - const mid = lines.map(l => "║ " + pad(l) + " ║").join("\n ") - - return ` ${top}\n ${mid}\n ${bot}` -} - -const Main = () => -{ - const title = `BENSON CLI v${pkg.version}` - const subtitle = `Fabric Server for MC ${minecraft_version}` - - const box = DrawBox([title, subtitle]) - - console.log(` -${box} - - Type 'help' for commands. -`) - - const rl = readline.createInterface - ({ - input: process.stdin, - output: process.stdout - }) - - const Prompt = () => - { - rl.question("benson> ", async (input) => - { - await ProcessCommand(input, rl) - Prompt() - }) - } - - rl.on("close", () => - { - console.log("\n Goodbye!\n") - process.exit(0) - }) - - Prompt() -} +module.exports = { Generate, serverDir } -Main() diff --git a/cli/commands/list.js b/cli/commands/list.js new file mode 100644 index 0000000..48bb2cf --- /dev/null +++ b/cli/commands/list.js @@ -0,0 +1,15 @@ +const { LoadConfig } = require("../../lib/config") + +const config = LoadConfig() +const { minecraft_version, modrinth_mods } = config + +const ListMods = () => +{ + console.log(`\n Mods for MC ${minecraft_version}:\n`) + for (const slug of modrinth_mods) + console.log(` - ${slug}`) + console.log(`\n Total: ${modrinth_mods.length} mods\n`) +} + +module.exports = { ListMods } + diff --git a/cli/commands/merge.js b/cli/commands/merge.js new file mode 100644 index 0000000..138dd54 --- /dev/null +++ b/cli/commands/merge.js @@ -0,0 +1,156 @@ +const fs = require("fs-extra") +const path = require("path") +const { spawn } = require("child_process") +const archiver = require("archiver") + +const { LoadConfig } = require("../../lib/config") +const { serverDir } = require("./generate") + +const config = LoadConfig() + +const +{ + server_location, + backup_location, + merge_pre_command, + merge_post_command +} = config + +const RunCommand = (command) => +{ + return new Promise((resolve) => + { + console.log(` Running: ${command}`) + + const proc = spawn(command, [], { shell: true, stdio: "inherit" }) + + proc.on("close", (code) => + { + if (code === 0) + console.log(" Command completed.") + else + console.log(` Command exited with code ${code}`) + resolve(code) + }) + + proc.on("error", (err) => + { + console.error(" Command failed:", err.message) + resolve(1) + }) + }) +} + +const CreateBackup = (sourceDir, backupDir) => +{ + return new Promise(async (resolve, reject) => + { + const now = new Date() + const timestamp = now.toISOString() + .replace(/[:.]/g, "-") + .replace("T", "_") + .slice(0, 19) + + const zipName = `backup_${timestamp}.zip` + const zipPath = path.join(backupDir, zipName) + + await fs.ensureDir(backupDir) + + const output = fs.createWriteStream(zipPath) + const archive = archiver("zip", { zlib: { level: 9 } }) + + output.on("close", () => + { + console.log(` Created backup: ${zipName} (${archive.pointer()} bytes)`) + resolve(zipPath) + }) + + archive.on("error", err => reject(err)) + + archive.pipe(output) + archive.directory(sourceDir, false) + archive.finalize() + }) +} + +const Merge = async () => +{ + if (!server_location || !backup_location) + { + console.log(" Error: server_location and backup_location must be set in config.") + return + } + + if (!await fs.pathExists(serverDir)) + { + console.log(" Error: No generated files found. Run 'generate' first.") + return + } + + // Run pre-command + if (merge_pre_command) + { + console.log("\n Running pre-merge command...") + await RunCommand(merge_pre_command) + console.log("") + } + + // Check if server location exists and back it up + if (await fs.pathExists(server_location)) + { + console.log(` Backing up ${server_location}...`) + try + { + await CreateBackup(server_location, backup_location) + } + catch (err) + { + console.error(" Failed to create backup:", err.message) + return + } + } + else + { + console.log(" Server location doesn't exist, creating it...") + await fs.ensureDir(server_location) + } + + // Move generated files to server location + console.log(" Moving generated files to server location...") + try + { + const items = await fs.readdir(serverDir) + for (const item of items) + { + const src = path.join(serverDir, item) + const dest = path.join(server_location, item) + + // Remove existing item at destination + if (await fs.pathExists(dest)) + await fs.remove(dest) + + await fs.move(src, dest) + console.log(` Moved: ${item}`) + } + + // Remove the now-empty server directory + await fs.remove(serverDir) + + console.log(" Merge complete!") + } + catch (err) + { + console.error(" Failed to move files:", err.message) + return + } + + // Run post-command + if (merge_post_command) + { + console.log("\n Running post-merge command...") + await RunCommand(merge_post_command) + } +} + +module.exports = { Merge } + diff --git a/config/config.json b/config/config.json index 67011f9..e7c0844 100644 --- a/config/config.json +++ b/config/config.json @@ -4,20 +4,12 @@ "minecraft_version": "1.21.10", "fabric_loader_version": "0.17.3", "loader_meta_version": "1.1.0", - "java_path": "C:/Program Files/Java/jdk-25/bin/java.exe", + "java_path": null, - "modrinth_mods": [ - "audioplayer", - "c2me-fabric", - "convenient-mobgriefing", - "fabric-api", - "image2map", - "lithium", - "simple-voice-chat", - "cloth-config", - "ferrite-core", - "player-roles", - "distanthorizons", - "spawn-radar" - ] -}
\ No newline at end of file + "server_location": null, + "backup_location": null, + "merge_pre_command": null, + "merge_post_command": null, + + "modrinth_mods": [ ] +} diff --git a/package.json b/package.json index 560bb6a..ad276ad 100644 --- a/package.json +++ b/package.json @@ -16,14 +16,15 @@ "main": "bot/app.js", "scripts": { "bot": "node bot/app.js", - "benson": "node updater/cli.js" + "benson": "node cli/app.js" }, "dependencies": { + "archiver": "^7.0.1", "axios": "^1.12.2", "discord.js": "^14.23.2", "fs-extra": "^11.3.2" }, "bin": { - "benson": "./updater/cli.js" + "benson": "./cli/app.js" } } |
