From 9ac2bbe9781f7ab5a798621cc9dd46b4ff8befda Mon Sep 17 00:00:00 2001 From: destruc7i0n Date: Tue, 4 Feb 2020 00:51:15 -0500 Subject: Refactor and rebuild to TypeScript --- src/Rcon.ts | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/Rcon.ts (limited to 'src/Rcon.ts') diff --git a/src/Rcon.ts b/src/Rcon.ts new file mode 100644 index 0000000..81cf3ea --- /dev/null +++ b/src/Rcon.ts @@ -0,0 +1,113 @@ +// Credits to M4GNV5 for this library + +import net from 'net' + +class Rcon { + socket: net.Socket + timeout: number + nextId: number + + connected: boolean + authed: boolean + debug: boolean + + ip: string + port: number + + packages: any + + constructor (ip: string, port: number, debug: boolean) { + this.ip = ip + this.port = port + this.debug = debug + + this.timeout = 5000 + this.nextId = 0 + this.connected = false + this.authed = false + this.packages = [] + + this.socket = net.connect(port, ip, () => { + this.connected = true + console.log('[INFO] Authenticated with ' + ip + ':' + port) + }) + + this.socket.on('data', (data) => { + const id = data.readInt32LE(4) + const type = data.readInt32LE(8) + const response = data.toString('ascii', 12, data.length - 2) + + if (this.packages[id]) { + this.packages[id](type, response) + } else { + console.log('Unexpected rcon response', id, type, response) + } + }).on('end', () => { + if (debug) { + console.log('[DEBUG] Rcon closed!') + } + }) + } + + close () { + this.connected = false + this.socket.end() + } + + async auth (password: string) { + if (this.authed) { throw new Error('Already authed') } + + if (this.connected){ + await this.sendPackage(3, password) + } else { + return new Promise(resolve => { + this.socket.on('connect', async () => { + await this.sendPackage(3, password) + resolve() + }) + }) + } + } + + command (cmd: string) { + return this.sendPackage(2, cmd) + } + + sendPackage (type: number, payload: string) { + const id = this.nextId + this.nextId++ + + if (!this.connected) { throw new Error('Cannot send package while not connected') } + + const length = 14 + payload.length + const buff = Buffer.alloc(length) + buff.writeInt32LE(length - 4, 0) + buff.writeInt32LE(id, 4) + buff.writeInt32LE(type, 8) + + buff.write(payload, 12) + buff.writeInt8(0, length - 2) + buff.writeInt8(0, length - 1) + + this.socket.write(buff) + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + delete this.packages[id] + return reject('Server sent no request in ' + this.timeout / 1000 + ' seconds') + }, this.timeout) + + this.packages[id] = (type: number, response: any) => { + clearTimeout(timeout) + const err = type >= 0 ? false : 'Server sent package code ' + type + if (this.debug) { + console.log('[DEBUG] Received response: ' + response) + } + if (err) return reject(err) + return resolve(response) + } + }) + } +} + +export default Rcon -- cgit 1.4.1 From 7c4dd4a8f126701e2a4a63bd9d860e2068941bd7 Mon Sep 17 00:00:00 2001 From: destruc7i0n Date: Tue, 4 Feb 2020 12:57:59 -0500 Subject: Update types --- README.md | 3 +-- src/Discord.ts | 22 ++++++++++------------ src/MinecraftHandler.ts | 18 +++++++++--------- src/Rcon.ts | 33 ++++++++++++++++++++++----------- src/Shulker.ts | 2 +- 5 files changed, 43 insertions(+), 35 deletions(-) (limited to 'src/Rcon.ts') diff --git a/README.md b/README.md index 68d7555..0601c79 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ - Can send messages regarding advancements, when players join and leave, and player deaths - Allows admins to send commands to Minecraft through Discord - ## Installation and usage Create a Discord bot here: https://discordapp.com/developers/applications/me @@ -67,7 +66,7 @@ You can also easily Deploy to Heroku and the like, just be sure to edit `YOUR_UR "SHOW_INIT_MESSAGE": true, /* Sends the message on boot if not a local file of what command to run */ - "ALLOW_USER_MENTIONS": false, /* should replace @mentions with the mention in discord */ + "ALLOW_USER_MENTIONS": false, /* should replace @mentions with the mention in discord (format: @username#discriminator) */ "ALLOW_HERE_EVERYONE_MENTIONS": false, /* replaces @everyone and @here with "@ everyone" and "@ here" respectively */ "ALLOW_SLASH_COMMANDS": false, /* whether to allow users to run slash commands from discord */ "SLASH_COMMAND_ROLES": [], /* if the above is enabled, the names of the roles which can run slash commands */ diff --git a/src/Discord.ts b/src/Discord.ts index 7088c0e..a0a3498 100644 --- a/src/Discord.ts +++ b/src/Discord.ts @@ -28,7 +28,7 @@ class Discord { } } - async onMessage (message: Message) { + private async onMessage (message: Message) { // don't want to check other channels if (message.channel.id !== this.config.DISCORD_CHANNEL_ID || message.channel.type !== 'text') return // if using webhooks, ignore this! @@ -50,7 +50,7 @@ class Discord { if (this.config.ALLOW_SLASH_COMMANDS && this.config.SLASH_COMMAND_ROLES && message.cleanContent.startsWith('/')) { const author = message.member if (author.roles.find(r => this.config.SLASH_COMMAND_ROLES.includes(r.name))) { - // raw command, can be dangerous... + // send the raw command, can be dangerous... command = message.cleanContent } else { console.log('[INFO] User attempted a slash command without a role') @@ -68,7 +68,7 @@ class Discord { rcon.close() } - makeMinecraftTellraw(message: Message): string { + private makeMinecraftTellraw(message: Message): string { const username = emojiStrip(message.author.username) const discriminator = message.author.discriminator const text = emojiStrip(message.cleanContent) @@ -81,7 +81,7 @@ class Discord { .replace('%message%', variables.text) } - replaceDiscordMentions(message: string): string { + private replaceDiscordMentions(message: string): string { const possibleMentions = message.match(/@(\S+)/gim) if (possibleMentions) { for (let mention of possibleMentions) { @@ -91,9 +91,7 @@ class Discord { if (this.config.ALLOW_USER_MENTIONS) { const user = this.client.users.find(user => user.username === username && user.discriminator === mentionParts[1]) if (user) { - if (this.config.ALLOW_USER_MENTIONS) { - message = message.replace(mention, '<@' + user.id + '>') - } + message = message.replace(mention, '<@' + user.id + '>') } } } @@ -111,13 +109,13 @@ class Discord { return message } - makeDiscordWebhook (username: string, message: string) { + private makeDiscordWebhook (username: string, message: string) { message = this.replaceDiscordMentions(message) let avatarURL - if (username === this.config.SERVER_NAME + ' - Server') { // Use avatar for the server + if (username === this.config.SERVER_NAME + ' - Server') { // use avatar for the server avatarURL = this.config.SERVER_IMAGE || 'https://minotar.net/helm/Steve/256.png' - } else { // Use avatar for player + } else { // use avatar for player avatarURL = `https://minotar.net/helm/${username}/256.png` } @@ -128,7 +126,7 @@ class Discord { } } - makeDiscordMessage(username: string, message: string) { + private makeDiscordMessage(username: string, message: string) { message = this.replaceDiscordMentions(message) return this.config.DISCORD_MESSAGE_TEMPLATE @@ -136,7 +134,7 @@ class Discord { .replace('%message%', message) } - async sendMessage (username: string, message: string) { + public async sendMessage (username: string, message: string) { if (this.config.USE_WEBHOOKS) { const webhook = this.makeDiscordWebhook(username, message) try { diff --git a/src/MinecraftHandler.ts b/src/MinecraftHandler.ts index aa4f2f7..7ed9d86 100644 --- a/src/MinecraftHandler.ts +++ b/src/MinecraftHandler.ts @@ -21,7 +21,7 @@ class MinecraftHandler { this.config = config } - fixMinecraftUsername (username: string) { + private fixMinecraftUsername (username: string) { return username.replace(/(ยง[A-Z-a-z0-9])/g, '') } @@ -50,6 +50,7 @@ class MinecraftHandler { const logLine = logLineData[1] + // the username used for server messages const serverUsername = `${this.config.SERVER_NAME} - Server` if (logLine.startsWith('<')) { @@ -117,9 +118,8 @@ class MinecraftHandler { private initWebServer (callback: Callback) { // init the webserver this.app = express() - const http = require('http').Server(this.app) - this.app.use((request: express.Request, response: express.Response, next: express.NextFunction) => { + this.app.use((request, response, next) => { request.rawBody = '' request.setEncoding('utf8') @@ -140,9 +140,9 @@ class MinecraftHandler { res.json({ received: true }) }) - const port = process.env.PORT || this.config.PORT + const port: number = Number(process.env.PORT) || this.config.PORT - http.listen(port, () => { + this.app.listen(port, () => { console.log('[INFO] Bot listening on *:' + port) if (!this.config.IS_LOCAL_FILE && this.config.SHOW_INIT_MESSAGE) { @@ -156,10 +156,10 @@ class MinecraftHandler { private initTail (callback: Callback) { if (fs.existsSync(this.config.LOCAL_FILE_PATH)) { - console.log(`[INFO] Using configuration for local file at "${this.config.LOCAL_FILE_PATH}"`) + console.log(`[INFO] Using configuration for local log file at "${this.config.LOCAL_FILE_PATH}"`) this.tail = new Tail(this.config.LOCAL_FILE_PATH) } else { - throw new Error(`[ERROR] Local file not found at "${this.config.LOCAL_FILE_PATH}"`) + throw new Error(`[ERROR] Local log file not found at "${this.config.LOCAL_FILE_PATH}"`) } this.tail.on('line', (data: string) => { // Parse the line to see if we care about it @@ -169,11 +169,11 @@ class MinecraftHandler { } }) this.tail.on('error', (error: any) => { - console.log('[ERROR] Error tailing file: ' + error) + console.log('[ERROR] Error tailing log file: ' + error) }) } - init (callback: Callback) { + public init (callback: Callback) { if (this.config.IS_LOCAL_FILE) { this.initTail(callback) } else { diff --git a/src/Rcon.ts b/src/Rcon.ts index 81cf3ea..180ad38 100644 --- a/src/Rcon.ts +++ b/src/Rcon.ts @@ -14,7 +14,7 @@ class Rcon { ip: string port: number - packages: any + packages: { [key: number]: (type: number, response: string) => void } constructor (ip: string, port: number, debug: boolean) { this.ip = ip @@ -32,7 +32,7 @@ class Rcon { console.log('[INFO] Authenticated with ' + ip + ':' + port) }) - this.socket.on('data', (data) => { + this.socket.on('data', (data: Buffer) => { const id = data.readInt32LE(4) const type = data.readInt32LE(8) const response = data.toString('ascii', 12, data.length - 2) @@ -49,31 +49,42 @@ class Rcon { }) } - close () { + public close () { this.connected = false this.socket.end() } - async auth (password: string) { + public async auth (password: string): Promise { if (this.authed) { throw new Error('Already authed') } if (this.connected){ - await this.sendPackage(3, password) + try { + await this.sendPackage(3, password) + } catch (e) { + console.log('[ERROR] Could not send password to Rcon server!') + if (this.debug) console.error(e) + } } else { - return new Promise(resolve => { + return new Promise((resolve, reject) => { this.socket.on('connect', async () => { - await this.sendPackage(3, password) - resolve() + try { + await this.sendPackage(3, password) + resolve() + } catch (e) { + console.log('[ERROR] Could not send password to Rcon server!') + if (this.debug) console.error(e) + reject(e) + } }) }) } } - command (cmd: string) { + public command (cmd: string): Promise { return this.sendPackage(2, cmd) } - sendPackage (type: number, payload: string) { + public sendPackage (type: number, payload: string): Promise { const id = this.nextId this.nextId++ @@ -97,7 +108,7 @@ class Rcon { return reject('Server sent no request in ' + this.timeout / 1000 + ' seconds') }, this.timeout) - this.packages[id] = (type: number, response: any) => { + this.packages[id] = (type: number, response: string) => { clearTimeout(timeout) const err = type >= 0 ? false : 'Server sent package code ' + type if (this.debug) { diff --git a/src/Shulker.ts b/src/Shulker.ts index 16921d6..4c79c78 100644 --- a/src/Shulker.ts +++ b/src/Shulker.ts @@ -23,7 +23,7 @@ class Shulker { if (this.config.USE_WEBHOOKS) { console.log('[INFO] Using Discord WebHooks to send messages') } else { - console.log('[INFO] Using Discord bot to send messages') + console.log('[INFO] Using the Discord bot to send messages') } return true -- cgit 1.4.1