import {MatrixClient, SimpleFsStorageProvider} from "@vector-im/matrix-bot-sdk"; import {matrix} from "./config.js"; import "colors"; import {spawn} from "child_process"; import escapeHTML from "escape-html"; // matrix var storage = new SimpleFsStorageProvider("matrix-data.json"); var client = new MatrixClient(matrix.homeserver_url, matrix.access_token, storage); var sendmsg = body => client.sendMessage(matrix.room_id, {body, msgtype: "m.text", format: "org.matrix.custom.html", formatted_body: escapeHTML(body), "m.mentions": {}}); await client.start(); console.log("matrix ready"); // minecraft var mc_ready = false; var mc = spawn("java", ["-Xmx16G", "-Xms16G", "-jar", "server.jar"], { cwd: "server" }); mc.stdout.on("data", data => { var line = data.toString().trim(); console.log(line.green); if (line.endsWith(`[Server thread/INFO]: No player was found`)) return; if (/^\[\d\d:\d\d:\d\d\] \[Server thread\/INFO\]: Done/gm.test(line)) { mc_ready = true; return; } if (/^\[\d\d:\d\d:\d\d\] \[Server thread\/INFO\]: Stopping (?:the )?server/gm.test(line)) { mc_ready = false; return; } if (!mc_ready) return; if (/^\[\d\d:\d\d:\d\d\] \[Netty (?:Server|Epoll|Kqueue) IO #\d+\/ERROR]:/.test(line)) return; line = line.replace(/^\[\d\d:\d\d:\d\d\] \[(?:Server thread|ServerMain)\/(?:INFO|WARN)\]: (?:\[Not Secure\] )?/gm, ''); if (line.startsWith("Can't keep up!")) return; sendmsg(line).catch(console.error); }); mc.stderr.on("data", data => { var line = data.toString().trim(); console.log(line.yellow); }); mc.on("close", code => { mc_ready = false; console.log("mc exit", code); process.exit(code); }) process.stdin.on("data", data => { mc.stdin.write(data); }); // matrix to minecraft client.on("room.message", async (roomId, event) => { if (roomId != matrix.room_id) return; if (event.sender == await client.getUserId()) return; var msg = event.content?.body; if (typeof msg !== "string") return; if (event.content.filename && event.content.filename != event.content.body) msg += `\n${event.content.filename}`; let displayname = await getDisplayName(event.sender); msg = `§b§l${displayname}:§r§b ${msg}`; var replied_event_id = event.content["m.relates_to"]?.["m.in_reply_to"]?.event_id; if (replied_event_id) { try { var replied_event = await client.getEvent(roomId, replied_event_id); let displayname = await getDisplayName(replied_event.sender); msg = `§3> §l${displayname}:§r§3 §o${replied_event.content?.body || ''}§r\n${msg}`; } catch(error) { console.error(error.message); msg = `§3> §o§4failed to load replied message: ${error.message}§r\n${msg}`; } } console.log("[matrix]", msg.blue); if (!mc_ready) return; mc.stdin.write(`tellraw @a ${JSON.stringify({text: msg, color: "aqua"})}\n`); if (event.content.body.toLowerCase() == "list") mc.stdin.write("list\n"); }); // util var displaynames = new Map(); async function getDisplayName(userId) { var displayname = displaynames.get(userId); if (!displayname) { try { var {displayname} = await client.getUserProfile(userId); } catch (error) { console.error(error.message); } if (!displayname) displayname = userId.match(/^@(.+):/)?.[1] || userId; displaynames.set(userId, displayname); setTimeout(() => { displaynames.delete(userId); }, 60000); } return displayname; }