diff --git a/src/modules/misc.js b/src/modules/misc.js index e9d77fe..b5f3ca6 100644 --- a/src/modules/misc.js +++ b/src/modules/misc.js @@ -11,6 +11,7 @@ const { const GoogleImages = require("google-images"); const {tinycolor, random: randomColor} = require("@ctrl/tinycolor"); const sharp = require("sharp"); +const net = require("node:net"); const imagesClient = new GoogleImages(hf.apikeys.gimg, hf.apikeys.google); @@ -682,3 +683,155 @@ color.callback = async function (msg, line, args, {truerandom}) { }; }; hf.registerCommand(color); + +function writeVarInt(value) { + let buf = Buffer.alloc(0); + + do { + let temp = value & 0b01111111; + + value >>>= 7; + + if (value != 0) { + temp |= 0b10000000; + } + + buf = Buffer.concat([buf, Buffer.from([temp])]); + } while (value != 0); + + return buf; +} + +const ident = Buffer.from("HiddenPhox (c7.pm) ", "utf8"); +const identPort = Buffer.alloc(2); +identPort.writeUInt16BE(3); + +const handshake = Buffer.concat([ + writeVarInt(0x0), + writeVarInt(1073741953), + writeVarInt(ident.length), + ident, + identPort, + writeVarInt(1), +]); +const handshakeWithLength = Buffer.concat([ + writeVarInt(handshake.length), + handshake, +]); + +const status = Buffer.concat([writeVarInt(1), writeVarInt(0x0)]); + +const HANDSHAKE_PACKET = Buffer.concat(handshakeWithLength, status); + +const formattingToAnsi = { + r: "0", + l: "1", + m: "9", + n: "4", + o: "3", + 0: "30", + 1: "34", + 2: "32", + 3: "36", + 4: "31", + 5: "35", + 6: "33", + 7: "37", + 8: "90", + 9: "94", + a: "92", + b: "96", + c: "91", + d: "95", + e: "93", + f: "97", +}; + +const mcserver = new Command("mcserver"); +mcserver.category = CATEGORY; +mcserver.helpText = "Query a Minecraft server"; +mcserver.callback = async function (msg, line) { + if (!line || line == "") return "Arguments required."; + + const [ip, port] = line.split(":"); + + const data = await new Promise((resolve, reject) => { + const timeout = setTimeout(() => resolve("timeout"), 5000); + + const client = net.createConnection({ + host: ip, + port: port ?? 25565, + timeout: 5000, + }); + client.on("connect", async function () { + client.write(HANDSHAKE_PACKET); + }); + let totalData = Buffer.alloc(0); + client.on("data", function (data) { + totalData = Buffer.concat(totalData, data); + }); + + client.on("close", function (err) { + if (err) return reject(err); + const dataAsString = totalData.toString().trim(); + console.log(dataAsString); + const json = JSON.parse( + dataAsString.slice( + dataAsString.indexOf("{"), + dataAsString.lastIndexOf("}") + 1 + ) + ); + clearTimeout(timeout); + return resolve(json); + }); + }); + + if (data == "timeout") { + return "Timed out trying to query."; + } else { + const motd = data.description.text.replace( + /\u00a7([a-f0-9k-or])/gi, + (formatting) => { + const ansi = formattingToAnsi[formatting]; + return ansi ? `\x1b[${ansi}m` : ""; + } + ); + + const players = data.players?.sample?.map((player) => player.name) ?? []; + const totalPlayers = `(${data.players.online}/${data.players.max})`; + let image; + if (data.favicon) { + image = Buffer.from(data.favicon.slice(data.favicon.indexOf(","))); + } + + return { + embed: { + title: `Server info for: \`${line}\``, + fields: [ + { + name: "MOTD", + value: `\`\`\`ansi\n${motd}\n\`\`\``, + }, + { + name: "Version", + value: `${data.version.name} (\`${data.version.protocol}\`)`, + inline: true, + }, + { + name: `Players ${players.length > 0 ? totalPlayers : ""}`, + value: players.length > 0 ? players.join(", ") : totalPlayers, + inline: players.length == 0, + }, + ], + thumbnail: image && { + url: "attachment://icon.png", + }, + }, + file: { + file: image, + name: "icon.png", + }, + }; + } +}; +hf.registerCommand(mcserver);