mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-12 07:54:52 +09:00
Players on the watch list are announced to every online admin on every instance when they join, and the join is posted to the Discord and JSON webhooks. The Reports page has a second table for the watch list with an Add button and a Delete button per row. Reports can be marked read or unread from the web interface. Each report row also has an Add to watch list button which opens the add form filled in with the player and reason. Setting exp_reports.watch_unread_reports treats players with unread reports as watched. Reports now store created_at_ms because marking them read changes updated_at_ms. Old reports.json files migrate on load with updated_at_ms as the creation time. The report webhook moved from the datastore update listener to the create handler so reading a report is not posted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
102 lines
3.4 KiB
TypeScript
102 lines
3.4 KiB
TypeScript
import type { InstancePluginContext } from "@clusterio/host";
|
|
import * as messages from "./messages.js";
|
|
|
|
/** Sent by the lua side when a player reports another. */
|
|
export type IpcReportCreate = {
|
|
player_name: string,
|
|
by_player_name: string,
|
|
reason: string,
|
|
};
|
|
|
|
/** Sent by the lua side to list the reports for a player, or for everyone. */
|
|
export type IpcReportList = {
|
|
caller: string,
|
|
player_name: string | undefined,
|
|
};
|
|
|
|
/** Sent by the lua side to delete the reports against a player, optionally only those from one player. */
|
|
export type IpcReportDelete = {
|
|
caller: string,
|
|
player_name: string,
|
|
by_player_name: string | undefined,
|
|
};
|
|
|
|
/**
|
|
* Bridges the lua side and the controller.
|
|
*
|
|
* Nothing is kept in the lua state, every request goes to the controller and
|
|
* the answer is handed back to lua once it arrives. The instance may have
|
|
* stopped by then, in which case the answer is dropped.
|
|
*/
|
|
export default async function (context: InstancePluginContext) {
|
|
const { instance } = context;
|
|
|
|
/** Hand an answer to lua, unless the instance stopped while it was being fetched. */
|
|
async function luaSend(receiver: string, json: any) {
|
|
if (instance.status !== "running") {
|
|
return;
|
|
}
|
|
const text = JSON.stringify(json);
|
|
// Reasons can contain anything, so pick a long bracket level the json does not close
|
|
let level = "=";
|
|
while (text.includes(`]${level}]`)) {
|
|
level += "=";
|
|
}
|
|
await instance.sendRcon(
|
|
`/sc exp_reports.${receiver}(helpers.json_to_table[${level}[${text}]${level}])`, true
|
|
);
|
|
}
|
|
|
|
instance.server.handle("exp_reports:create", async (event: IpcReportCreate) => {
|
|
try {
|
|
const report = await instance.sendTo("controller", new messages.ReportCreateRequest(
|
|
event.player_name, event.reason, event.by_player_name,
|
|
));
|
|
const reports = await instance.sendTo("controller", new messages.ReportListRequest(event.player_name));
|
|
await luaSend("receive_created", {
|
|
report: report.toJSON(),
|
|
reports: reports.map(other => other.toJSON()),
|
|
});
|
|
} catch (err: any) {
|
|
await luaSend("receive_error", { caller: event.by_player_name, message: err.message });
|
|
}
|
|
});
|
|
|
|
instance.server.handle("exp_reports:list", async (event: IpcReportList) => {
|
|
try {
|
|
const reports = await instance.sendTo("controller", new messages.ReportListRequest(event.player_name));
|
|
await luaSend("receive_list", {
|
|
caller: event.caller,
|
|
player_name: event.player_name,
|
|
reports: reports.map(report => report.toJSON()),
|
|
});
|
|
} catch (err: any) {
|
|
await luaSend("receive_error", { caller: event.caller, message: err.message });
|
|
}
|
|
});
|
|
|
|
instance.server.handle("exp_reports:delete", async (event: IpcReportDelete) => {
|
|
try {
|
|
let reports = await instance.sendTo("controller", new messages.ReportListRequest(event.player_name));
|
|
if (event.by_player_name !== undefined) {
|
|
reports = reports.filter(report => report.byPlayerName === event.by_player_name);
|
|
}
|
|
for (const report of reports) {
|
|
await instance.sendTo("controller", new messages.ReportDeleteRequest(report.id));
|
|
}
|
|
await luaSend("receive_deleted", {
|
|
caller: event.caller,
|
|
player_name: event.player_name,
|
|
by_player_name: event.by_player_name,
|
|
count: reports.length,
|
|
});
|
|
} catch (err: any) {
|
|
await luaSend("receive_error", { caller: event.caller, message: err.message });
|
|
}
|
|
});
|
|
|
|
instance.handle(messages.WatchedPlayerJoinedEvent, async (event: messages.WatchedPlayerJoinedEvent) => {
|
|
await luaSend("receive_watched_joined", event.toJSON());
|
|
});
|
|
}
|