Franek-Le commited on
Commit
d6986d3
·
1 Parent(s): 87b2903

Initial commit

Browse files
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ node_modules
2
+ dist
3
+ coverage
4
+
5
+ .env
6
+ .env.*
7
+
8
+ *.log
9
+
10
+ .vscode/settings.json
.prettierrc ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "trailingComma": "es5",
5
+ "printWidth": 100
6
+ }
Dockerfile ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:22-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package*.json ./
6
+ RUN npm install
7
+
8
+ COPY . .
9
+
10
+ RUN npm run build
11
+
12
+ EXPOSE 7860
13
+
14
+ CMD ["npm", "start"]
config.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "minecraft_host": "NSMP2.play.hosting",
3
+ "minecraft_port": 25565,
4
+ "minecraft_version": "1.21.1"
5
+ }
eslint.config.mjs ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import js from "@eslint/js";
2
+ import globals from "globals";
3
+ import tseslint from "typescript-eslint";
4
+ import prettier from "eslint-config-prettier";
5
+
6
+ export default tseslint.config(
7
+ js.configs.recommended,
8
+
9
+ ...tseslint.configs.recommended,
10
+
11
+ {
12
+ files: ["**/*.ts"],
13
+
14
+ languageOptions: {
15
+ globals: {
16
+ ...globals.node
17
+ }
18
+ },
19
+
20
+ rules: {
21
+ "@typescript-eslint/no-explicit-any": "warn",
22
+ "@typescript-eslint/no-unused-vars": "off"
23
+ }
24
+ },
25
+
26
+ prettier
27
+ );
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "server",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "check": "prettier . --check",
8
+ "lint": "eslint .",
9
+ "start": "node dist/index.js",
10
+ "dev": "tsc --watch",
11
+ "format": "prettier . --write"
12
+ },
13
+ "keywords": [],
14
+ "author": "Franek_Le",
15
+ "license": "MIT",
16
+ "description": "",
17
+ "dependencies": {
18
+ "express": "^5.2.1",
19
+ "mineflayer": "^4.25.0",
20
+ "tslib": "^2.8.1"
21
+ },
22
+ "devDependencies": {
23
+ "@eslint/js": "^9.39.4",
24
+ "@types/express": "^5.0.6",
25
+ "@types/node": "^25.9.3",
26
+ "eslint": "^9.39.4",
27
+ "eslint-config-prettier": "^10.1.8",
28
+ "globals": "^17.6.0",
29
+ "prettier": "^3.8.4",
30
+ "typescript": "^6.0.3",
31
+ "typescript-eslint": "^8.61.1"
32
+ }
33
+ }
src/bot/bot.ts ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import mineflayer, { BotOptions, Bot as MineflayerBot } from "mineflayer";
2
+ import { Config } from "../config/config";
3
+ import { Logger } from "../logger/logger";
4
+
5
+ export class Bot {
6
+ private bot: MineflayerBot | undefined = undefined;
7
+ private readonly botArgs: BotOptions;
8
+ private readonly config: Config;
9
+ private readonly logger: Logger;
10
+
11
+ constructor(config: Config, logger: Logger) {
12
+ this.config = config;
13
+ this.logger = logger;
14
+
15
+ this.botArgs = {
16
+ username: "AFKBot",
17
+ auth: "offline",
18
+ version: this.config.get<string>("minecraft_version"),
19
+ chatLengthLimit: 512,
20
+ host: this.config.get<string>("minecraft_host"),
21
+ port: this.config.get<number>("minecraft_port"),
22
+ }
23
+ }
24
+
25
+ public connect() {
26
+ this.initBot();
27
+ }
28
+
29
+ private initBot() {
30
+ this.bot = mineflayer.createBot(this.botArgs);
31
+
32
+ this.bot.on("login", () => {
33
+ this.logger.info(`[Minecraft Bot]: Connected to: ${this.botArgs.host}:${this.botArgs.port}`);
34
+ })
35
+
36
+ this.bot.on("end", () => {
37
+ this.logger.info("[Minecraft Bot]: Disconnected.")
38
+
39
+ this.logger.info("[Minecraft Bot]: Reconnecting...")
40
+ setTimeout(this.initBot, 5000);
41
+ })
42
+
43
+ this.bot.on("kicked", (reason: string, _loggedIn: boolean) => {
44
+ this.logger.warn(`[Minecraft Bot]: Kicked for ${reason}`);
45
+
46
+ this.logger.info("[Minecraft Bot]: Reconnecting...")
47
+ setTimeout(this.initBot, 5000);
48
+ })
49
+
50
+ this.bot.on("error", (error: unknown) => {
51
+ if (typeof error === "object" && error !== null && "code" in error && (error as { code: string }).code === "ECONNREFUSED") {
52
+ this.logger.error(`[Minecraft Bot]: Failed to connect to: ${this.botArgs.host}:${this.botArgs.port}`);
53
+ } else {
54
+ this.logger.error(`[Minecraft Bot]: Unhandled error: ${error}`);
55
+ }
56
+ })
57
+ }
58
+ }
src/config/config.ts ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+
4
+ export class Config {
5
+ private readonly data: Record<string, string | object | number | []>;
6
+
7
+ constructor(data: Record<string, any>) {
8
+ this.data = data;
9
+ }
10
+
11
+ public static load(fileName = "config.json"): Config {
12
+ const configPath = Config.findConfigFile(fileName);
13
+
14
+ if (!configPath) {
15
+ throw new Error(`Could not find ${fileName}`);
16
+ }
17
+
18
+ const contents = fs.readFileSync(configPath, "utf8");
19
+ const data = JSON.parse(contents);
20
+
21
+ return new Config(data);
22
+ }
23
+
24
+ public get<T>(key: string): T {
25
+ if (!(key in this.data)) {
26
+ throw new Error(`Missing configuration key: ${key}`);
27
+ }
28
+
29
+ return this.data[key] as T;
30
+ }
31
+
32
+ public has(key: string): boolean {
33
+ return key in this.data;
34
+ }
35
+
36
+ private static findConfigFile(fileName: string): string | null {
37
+ let currentDir = process.cwd();
38
+
39
+ while (true) {
40
+ const candidate = path.join(currentDir, fileName);
41
+
42
+ if (fs.existsSync(candidate)) {
43
+ return candidate;
44
+ }
45
+
46
+ const parentDir = path.dirname(currentDir);
47
+
48
+ if (parentDir === currentDir) {
49
+ return null;
50
+ }
51
+
52
+ currentDir = parentDir;
53
+ }
54
+ }
55
+ }
src/index.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Server } from "./server/server";
2
+ import { Logger } from "./logger/logger";
3
+ import { Config } from "./config/config";
4
+ import { Bot } from "./bot/bot";
5
+
6
+ function main(): void {
7
+ const config: Config = new Config({});
8
+ const logger: Logger = new Logger();
9
+
10
+ const server: Server = new Server(logger);
11
+ const bot: Bot = new Bot(config, logger);
12
+
13
+ server.get("/", () => {
14
+ return "<h1>NSMP Server</h1>";
15
+ })
16
+
17
+ //bot.connect();
18
+ server.start(7860);
19
+ }
20
+
21
+ setImmediate(main);
src/logger/logger.ts ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ enum Color {
4
+ Reset = "\x1b[0m",
5
+ Gray = "\x1b[90m",
6
+ Blue = "\x1b[34m",
7
+ Yellow = "\x1b[33m",
8
+ Red = "\x1b[31m",
9
+ }
10
+
11
+ export class Logger {
12
+ constructor(private readonly context?: string) {}
13
+
14
+ debug(message: string, ...meta: unknown[]): void {
15
+ this.log("DEBUG", Color.Gray, message, meta);
16
+ }
17
+
18
+ info(message: string, ...meta: unknown[]): void {
19
+ this.log("INFO ", Color.Blue, message, meta);
20
+ }
21
+
22
+ warn(message: string, ...meta: unknown[]): void {
23
+ this.log("WARN ", Color.Yellow, message, meta);
24
+ }
25
+
26
+ error(message: string, ...meta: unknown[]): void {
27
+ this.log("ERROR", Color.Red, message, meta);
28
+ }
29
+
30
+ private log(level: string, color: Color, message: string, meta: unknown[]): void {
31
+ const timestamp = new Date().toISOString();
32
+ const context = this.context ? ` [${this.context}]` : "";
33
+
34
+ console.log(
35
+ `${color}[${timestamp}] ${level}${context}: ${message}${Color.Reset}`,
36
+ ...meta
37
+ );
38
+ }
39
+ }
src/server/server.ts ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import express, {Application, RequestHandler} from "express";
2
+ import { Server as HttpServer } from "http";
3
+ import { Logger } from "../logger/logger";
4
+
5
+ export class Server {
6
+ private readonly app: Application;
7
+ private server?: HttpServer;
8
+ private readonly logger: Logger;
9
+
10
+ constructor(logger: Logger) {
11
+ this.logger = logger;
12
+
13
+ this.app = express();
14
+
15
+ this.app.use(express.json());
16
+ this.app.use(express.urlencoded({ extended: true }));
17
+ }
18
+
19
+ public start(port: number): Promise<void> {
20
+ return new Promise((resolve, reject) => {
21
+ if (this.server) {
22
+ return reject(new Error("Server is already running"));
23
+ }
24
+
25
+ this.server = this.app.listen(port, () => {
26
+ this.logger.info(`[Server]: Listening on port ${port}`);
27
+ resolve();
28
+ });
29
+
30
+ this.server.on("error", reject);
31
+ });
32
+ }
33
+
34
+ public stop(): Promise<void> {
35
+ return new Promise((resolve, reject) => {
36
+ if (!this.server) {
37
+ return resolve();
38
+ }
39
+
40
+ this.server.close((err) => {
41
+ if (err) {
42
+ return reject(err);
43
+ }
44
+
45
+ this.server = undefined;
46
+ resolve();
47
+ });
48
+ });
49
+ }
50
+
51
+ public get(path: string, handler: RequestHandler): void {
52
+ this.app.get(path, handler);
53
+ }
54
+
55
+ public post(path: string, handler: RequestHandler): void {
56
+ this.app.post(path, handler);
57
+ }
58
+ }
tsconfig.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+
7
+ "rootDir": "./src",
8
+ "outDir": "./dist",
9
+
10
+ "strict": true,
11
+
12
+ "noImplicitAny": true,
13
+ "noUnusedLocals": true,
14
+ "noUnusedParameters": true,
15
+ "noImplicitReturns": true,
16
+ "noFallthroughCasesInSwitch": true,
17
+
18
+ "sourceMap": true,
19
+ "declaration": true,
20
+
21
+ "esModuleInterop": true,
22
+ "forceConsistentCasingInFileNames": true,
23
+ "skipLibCheck": true
24
+ },
25
+
26
+ "include": [
27
+ "src/**/*"
28
+ ],
29
+
30
+ "exclude": [
31
+ "node_modules",
32
+ "dist"
33
+ ]
34
+ }