Push module credits at end-of-session
endSession() now pushes the world's active module list to Frappe after
the snapshot push. Each module is projected to {id, title, url,
authors[].name + url} — versions and compatibility info are intentionally
omitted so the eventual public credits page doesn't leak dependency
versions. The Frappe endpoint also strips these fields server-side.
Module inventory push is treated as non-critical: a failure logs a
warning and adds a line to the chat summary, but doesn't abort the
session-completion flow. The chat summary now reports the count of
modules tracked.
Public API gains seitimeBridge.pushModuleInventory() for manual pushes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
c7159ee84b
commit
ed853de6a2
3 changed files with 57 additions and 4 deletions
|
|
@ -104,6 +104,38 @@ export async function pushSnapshot(actor) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Project each active Foundry module to a credit-only summary. Versions
|
||||
* are deliberately omitted: this list is intended for a public credits
|
||||
* page on the Frappe side and we don't want to leak dependency versions
|
||||
* if the site is ever indexed. The Frappe endpoint also strips any
|
||||
* version/compatibility fields server-side as defense-in-depth.
|
||||
*/
|
||||
function buildModuleInventory() {
|
||||
const out = [];
|
||||
for (const mod of game.modules.values()) {
|
||||
if (!mod.active) continue;
|
||||
const authors = (mod.authors || [])
|
||||
.map((a) => ({ name: a?.name, url: a?.url || null }))
|
||||
.filter((a) => a.name);
|
||||
out.push({
|
||||
id: mod.id,
|
||||
title: mod.title || mod.id,
|
||||
url: mod.url || null,
|
||||
authors,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export async function pushModuleInventory() {
|
||||
const modules = buildModuleInventory();
|
||||
console.log(`[${MODULE_ID}] pushing module inventory (${modules.length} active)`);
|
||||
const result = await bridgeFetch("receive_module_inventory", { modules });
|
||||
console.log(`[${MODULE_ID}] module inventory result:`, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function listScheduledSessions() {
|
||||
const result = await bridgeFetch("list_scheduled_sessions", {});
|
||||
return result?.message ?? [];
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { MODULE_ID } from "./constants.js";
|
|||
import {
|
||||
completeSession,
|
||||
listScheduledSessions,
|
||||
pushModuleInventory,
|
||||
pushSnapshot,
|
||||
} from "./api.js";
|
||||
|
||||
|
|
@ -91,7 +92,7 @@ async function pickSession(sessions) {
|
|||
});
|
||||
}
|
||||
|
||||
function buildSummaryHtml(completion, snapshotResult) {
|
||||
function buildSummaryHtml(completion, snapshotResult, moduleResult) {
|
||||
const parts = [
|
||||
`<h3>Session Complete</h3>`,
|
||||
`<p><b>${escapeHtml(completion.completed_session)}</b> marked Completed.</p>`,
|
||||
|
|
@ -110,6 +111,15 @@ function buildSummaryHtml(completion, snapshotResult) {
|
|||
.join("");
|
||||
parts.push(`<p style="color:#a00"><b>Failed pushes:</b></p><ul>${items}</ul>`);
|
||||
}
|
||||
if (moduleResult?.upserted) {
|
||||
parts.push(
|
||||
`<p>Tracked ${moduleResult.upserted} active module(s) for credits.</p>`,
|
||||
);
|
||||
} else if (moduleResult?.error) {
|
||||
parts.push(
|
||||
`<p style="color:#a00">Module inventory push failed: ${escapeHtml(moduleResult.error)}</p>`,
|
||||
);
|
||||
}
|
||||
return parts.join("");
|
||||
}
|
||||
|
||||
|
|
@ -152,8 +162,19 @@ export async function endSession() {
|
|||
|
||||
const snapshotResult = await pushAllSnapshots();
|
||||
|
||||
// Module inventory is non-critical — log and continue if the push fails
|
||||
// so the session still gets marked Completed with a clean summary.
|
||||
let moduleResult = null;
|
||||
try {
|
||||
const raw = await pushModuleInventory();
|
||||
moduleResult = raw?.message ?? null;
|
||||
} catch (err) {
|
||||
console.warn(`[${MODULE_ID}] module inventory push failed:`, err);
|
||||
moduleResult = { error: err.message };
|
||||
}
|
||||
|
||||
await ChatMessage.create({
|
||||
content: buildSummaryHtml(completion, snapshotResult),
|
||||
content: buildSummaryHtml(completion, snapshotResult, moduleResult),
|
||||
speaker: { alias: "Seitime Bridge" },
|
||||
whisper: ChatMessage.getWhisperRecipients("GM").map((u) => u.id),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import { MODULE_ID } from "./constants.js";
|
||||
import { registerSettings } from "./settings.js";
|
||||
import { pushManifest, pushSnapshot, testConnection } from "./api.js";
|
||||
import { pushManifest, pushModuleInventory, pushSnapshot, testConnection } from "./api.js";
|
||||
import { endSession, pushAllSnapshots } from "./macros.js";
|
||||
|
||||
let manifestTimerId = null;
|
||||
|
|
@ -62,7 +62,7 @@ Hooks.once("init", () => {
|
|||
|
||||
Hooks.once("ready", () => {
|
||||
const mod = game.modules.get(MODULE_ID);
|
||||
const api = { pushManifest, pushSnapshot, testConnection, endSession, pushAllSnapshots };
|
||||
const api = { pushManifest, pushSnapshot, pushModuleInventory, testConnection, endSession, pushAllSnapshots };
|
||||
mod.api = api;
|
||||
globalThis.seitimeBridge = api;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue