hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2025-12-09 23:02:55 +0100
committerhachem <im@hachem.wtf>2025-12-09 23:02:55 +0100
commit5cc4c5a689e124e3951c80f0cfda0de4a797fec7 (patch)
tree7cb412c67f54636a4926161f4c948b68d87e74e8
parent50e62e246fef7975866c244e08ead7c681d50349 (diff)
[add]: merging and refactor
-rw-r--r--README.md25
-rw-r--r--cli/app.js138
-rw-r--r--cli/commands/check.js81
-rw-r--r--cli/commands/clean.js80
-rw-r--r--cli/commands/generate.js (renamed from updater/cli.js)168
-rw-r--r--cli/commands/list.js15
-rw-r--r--cli/commands/merge.js156
-rw-r--r--config/config.json24
-rw-r--r--package.json5
9 files changed, 505 insertions, 187 deletions
diff --git a/README.md b/README.md
index 95b68ed..2d6698f 100644
--- a/README.md
+++ b/README.md
@@ -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"
}
}