rework main.js for a more OOP approach
This commit is contained in:
parent
9c8b5437b2
commit
c67cd50ff1
3 changed files with 139 additions and 109 deletions
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"database": "./data.sqlite",
|
||||
"minecraft": {
|
||||
"host": "localhost",
|
||||
"port": 25565,
|
||||
|
@ -7,6 +8,5 @@
|
|||
"auth": "microsoft",
|
||||
"username": "Notch"
|
||||
},
|
||||
"database": "./data.sqlite",
|
||||
"prefix": "!"
|
||||
}
|
||||
|
|
229
src/main.js
229
src/main.js
|
@ -21,113 +21,150 @@ import TPSCommand from "./commands/tps.js"
|
|||
import WorstPingCommand from "./commands/worst-ping.js"
|
||||
import JoinsCommand from "./commands/joins.js"
|
||||
|
||||
class Bot {
|
||||
|
||||
console.log(`____ _ ____ _ ____ _ `)
|
||||
console.log(`|___ \\| | |___ \\| | | _ \\ | | `)
|
||||
console.log(` __) | |__ __) | |_| |_) | ___ | |_ `)
|
||||
console.log(` |__ <| '_ \\|__ <| __| _ < / _ \\| __|`)
|
||||
console.log(` ___) | |_) |__) | |_| |_) | (_) | |_ `)
|
||||
console.log(`|____/|_.__/____/ \\__|____/ \\___/ \\__|`)
|
||||
console.log(` `)
|
||||
static #STOP_DISCONNECT_REASON = "__stop__9sE7pxOZub__"
|
||||
|
||||
let configPath = process.env["CONFIG_FILE"]
|
||||
#dbPath
|
||||
#clientFactory
|
||||
#prefix
|
||||
#dateFormat
|
||||
|
||||
#db
|
||||
#client
|
||||
|
||||
// Modules
|
||||
#chat
|
||||
#death
|
||||
#players
|
||||
#tps
|
||||
|
||||
#commands
|
||||
|
||||
constructor(dbPath, clientFactory, prefix) {
|
||||
this.#dbPath = dbPath
|
||||
this.#clientFactory = clientFactory
|
||||
this.#prefix = prefix
|
||||
this.#dateFormat = new Intl.DateTimeFormat("en-US", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "medium",
|
||||
timeZone: "UTC",
|
||||
})
|
||||
}
|
||||
|
||||
start() {
|
||||
console.log(`____ _ ____ _ ____ _ `)
|
||||
console.log(`|___ \\| | |___ \\| | | _ \\ | | `)
|
||||
console.log(` __) | |__ __) | |_| |_) | ___ | |_ `)
|
||||
console.log(` |__ <| '_ \\|__ <| __| _ < / _ \\| __|`)
|
||||
console.log(` ___) | |_) |__) | |_| |_) | (_) | |_ `)
|
||||
console.log(`|____/|_.__/____/ \\__|____/ \\___/ \\__|`)
|
||||
console.log(` `)
|
||||
|
||||
try {
|
||||
this.#db = new sqlite.DatabaseSync(this.#dbPath)
|
||||
console.debug("Opened database")
|
||||
|
||||
migrateDB(this.#db)
|
||||
console.debug("Migrated database")
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to open/migrate database: ${e}`)
|
||||
}
|
||||
|
||||
this.#connect()
|
||||
}
|
||||
|
||||
#connect() {
|
||||
this.#client = this.#clientFactory()
|
||||
|
||||
this.#chat = new Chat(this.#client)
|
||||
this.#chat.addPattern("chat", new RegExp(`^<([a-zA-Z0-9_]+)> (.+)$`))
|
||||
this.#chat.addPattern("whisper", new RegExp(`^\\[([a-zA-Z0-9_]+) -> me\\] (.+)$`))
|
||||
this.#chat.addListener("chat", this.#onChat.bind(this))
|
||||
|
||||
this.#death = new Death(this.#client)
|
||||
this.#death.addListener("death", this.#onDeath.bind(this))
|
||||
|
||||
this.#players = new Players(this.#client)
|
||||
this.#tps = new TPS(this.#client)
|
||||
|
||||
this.#client.addListener("connect", this.#onClientConnect.bind(this))
|
||||
this.#client.addListener("end", this.#onClientEnd.bind(this))
|
||||
|
||||
this.#commands = new Commands(this.#chat, this.#prefix)
|
||||
this.#commands.register(new BestPingCommand(this.#chat, this.#players))
|
||||
this.#commands.register(new HelpCommand(this.#chat, this.#commands))
|
||||
this.#commands.register(new InfoCommand(this.#chat))
|
||||
this.#commands.register(new JoinDateCommand(this.#chat, this.#db, this.#dateFormat))
|
||||
this.#commands.register(new JoinsCommand(this.#chat, this.#db))
|
||||
this.#commands.register(new PingCommand(this.#chat, this.#players))
|
||||
this.#commands.register(new PlaytimeCommand(this.#chat, this.#db))
|
||||
this.#commands.register(new SeenCommand(this.#chat, this.#db, this.#dateFormat))
|
||||
this.#commands.register(new TPSCommand(this.#chat, this.#tps))
|
||||
this.#commands.register(new WorstPingCommand(this.#chat, this.#players))
|
||||
|
||||
new PlayerTracker(this.#client, this.#players, this.#db)
|
||||
}
|
||||
|
||||
#onChat(message) {
|
||||
console.log(`Chat: ${message}`)
|
||||
}
|
||||
|
||||
#onDeath() {
|
||||
console.log("Client died")
|
||||
this.#death.respawn()
|
||||
}
|
||||
|
||||
#onClientConnect() {
|
||||
console.log("Client connected")
|
||||
}
|
||||
|
||||
#onClientEnd(reason) {
|
||||
this.#client = null
|
||||
if (reason == Bot.#STOP_DISCONNECT_REASON) {
|
||||
console.log("Client closed")
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`Client disconnected: ${reason}`)
|
||||
setTimeout(() => this.#connect(), 10_000)
|
||||
}
|
||||
|
||||
stop() {
|
||||
// Not much we can do if this fails
|
||||
try {
|
||||
this.#db.close()
|
||||
console.log("Database closed")
|
||||
} catch (ignored) { }
|
||||
|
||||
if (this.#client !== null) {
|
||||
this.#client.end(Bot.#STOP_DISCONNECT_REASON)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let configPath = process.env["CONFIG_PATH"]
|
||||
if (configPath === undefined) {
|
||||
console.log(`"CONFIG_FILE" not defined, defaulting to "./config.json"`)
|
||||
console.log("No \"CONFIG_PATH\" defined, defaulting to \"./config.json\"")
|
||||
configPath = "./config.json"
|
||||
}
|
||||
|
||||
let config
|
||||
try {
|
||||
const raw = await fs.readFile(configPath, "utf-8")
|
||||
const raw = await fs.readFile(configPath, "utf8")
|
||||
config = JSON.parse(raw)
|
||||
} catch (e) {
|
||||
console.error(`Failed to read config: ${e}`)
|
||||
process.exit(1)
|
||||
throw new Error(`Failed to reas config: ${e}`)
|
||||
}
|
||||
|
||||
const db = new sqlite.DatabaseSync(config["database"])
|
||||
console.log("Database opened")
|
||||
const bot = new Bot(
|
||||
config["database"],
|
||||
() => mcProtocol.createClient(config["minecraft"]),
|
||||
config["prefix"],
|
||||
)
|
||||
|
||||
migrateDB(db)
|
||||
|
||||
const reasonProcessExiting = "__processExiting__"
|
||||
const reconnectDelay = 10_000
|
||||
|
||||
const dateFormat = new Intl.DateTimeFormat("en-US", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "medium",
|
||||
timeZone: "UTC",
|
||||
bot.start()
|
||||
process.on("SIGINT", () => {
|
||||
console.log("Detected SIGINT, exiting")
|
||||
bot.stop()
|
||||
})
|
||||
|
||||
let client = null
|
||||
newClient()
|
||||
|
||||
function newClient() {
|
||||
console.log("Client connecting")
|
||||
|
||||
// Client
|
||||
client = mcProtocol.createClient(config["minecraft"])
|
||||
|
||||
client.addListener("connect", () => {
|
||||
console.log("Client connected")
|
||||
})
|
||||
|
||||
client.addListener("end", reason => {
|
||||
if (reason === reasonProcessExiting) {
|
||||
console.log("Client disconnected")
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`Client disconnected, reconnecting in ${reconnectDelay}ms: ${reason}`)
|
||||
setTimeout(() => newClient(), reconnectDelay)
|
||||
})
|
||||
|
||||
// Modules
|
||||
const chat = new Chat(client)
|
||||
const death = new Death(client)
|
||||
const players = new Players(client)
|
||||
const tps = new TPS(client)
|
||||
|
||||
chat.addPattern("chat", new RegExp(`^<([a-zA-Z0-9_]+)> (.+)$`))
|
||||
chat.addPattern("whisper", new RegExp(`^\\[([a-zA-Z0-9_]+) -> me\\] (.+)$`))
|
||||
|
||||
chat.addListener("chat", message => {
|
||||
console.log(`Chat: ${message}`)
|
||||
})
|
||||
|
||||
chat.addListener("chat:chat", (username, message) => { })
|
||||
chat.addListener("chat:whisper", (username, message) => { })
|
||||
|
||||
death.addListener("death", () => {
|
||||
console.log("Client died, respawning")
|
||||
death.respawn()
|
||||
})
|
||||
|
||||
new PlayerTracker(client, players, db)
|
||||
|
||||
// Commands
|
||||
const commands = new Commands(chat, config["prefix"])
|
||||
commands.register(new BestPingCommand(chat, players))
|
||||
commands.register(new HelpCommand(chat, commands))
|
||||
commands.register(new InfoCommand(chat))
|
||||
commands.register(new JoinDateCommand(chat, db, dateFormat))
|
||||
commands.register(new JoinsCommand(chat, db))
|
||||
commands.register(new PingCommand(chat, players))
|
||||
commands.register(new PlaytimeCommand(chat, db))
|
||||
commands.register(new SeenCommand(chat, db, dateFormat))
|
||||
commands.register(new TPSCommand(chat, tps))
|
||||
commands.register(new WorstPingCommand(chat, players))
|
||||
}
|
||||
|
||||
function end() {
|
||||
console.log("Detected end, quitting")
|
||||
client.end(reasonProcessExiting)
|
||||
|
||||
db.close()
|
||||
console.log("Database closed")
|
||||
}
|
||||
|
||||
process.on("SIGINT", end)
|
||||
process.on("SIGUSR1", end)
|
||||
process.on("SIGUSR2", end)
|
||||
process.on("uncaughtException", end)
|
||||
|
|
|
@ -10,26 +10,19 @@ const migrations = [
|
|||
]
|
||||
|
||||
export default function migrateDB(db) {
|
||||
const getStmt = db.prepare("PRAGMA user_version;")
|
||||
let version = getStmt.get()["user_version"]
|
||||
const version = db.prepare(`PRAGMA user_version;`).get()["user_version"]
|
||||
|
||||
for (let i = 0; i < migrations.length; i++) {
|
||||
const nextVersion = i + 1
|
||||
if (version >= nextVersion) {
|
||||
const newVersion = i + 1
|
||||
if (version >= newVersion) {
|
||||
continue
|
||||
}
|
||||
|
||||
console.log(`Migrating database to version ${nextVersion}`)
|
||||
try {
|
||||
db.exec(migrations[i])
|
||||
version = nextVersion
|
||||
|
||||
db.prepare(`PRAGMA user_version = ${version};`).run()
|
||||
db.prepare(`PRAGMA user_version = ${newVersion};`).run()
|
||||
} catch (e) {
|
||||
console.error(`Database migration to version ${nextVersion} failed: ${e}`)
|
||||
break
|
||||
throw new Error(`Failed to apply migration ${i}: ${e}`)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Database is migrated. Current version is ${version}`)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue