Files
2025-12-18 14:59:32 -08:00

111 lines
3.3 KiB
JavaScript

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;
}