feat(admin): forward in-game moderation to the website (bidirectional audit)
Phase C / §5.5: so the site's moderation log is complete regardless of origin, in-game uses of the write-plane verbs are forwarded as admin.audit (origin:"in-game"). - patches/commandlogging-event.patch: adds CommandLogging.OnWrite, raised in WriteLine before the m_Enabled guard so it fires even when file logging is off. Scripts-layer file -> dynamic build, no core rebuild. - patches/BridgeModerationAudit.cs: subscriber. Taps OnWrite for resolved ban/kick (parsing the target from the log line) and EventSink.Command for [bcast. Lives in patches/ (not overlay/) because it references OnWrite, which only exists post-patch — same rule as BridgeVendorSale.cs. - tools/scaffolding/BridgeAuditProbe.cs: gated headless verification. Verified live: a genuine [bcast plus simulated ban/kick log lines produced admin.audit frames with origin=in-game, actor, and the target parsed (seed_010); a non-moderation line was correctly ignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
This commit is contained in:
71
tools/scaffolding/BridgeAuditProbe.cs
Normal file
71
tools/scaffolding/BridgeAuditProbe.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
|
||||
using Server.Accounting;
|
||||
using Server.Commands;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Custom
|
||||
{
|
||||
/// <summary>
|
||||
/// Exercises the in-game moderation-audit forwarding (BridgeModerationAudit) without a game
|
||||
/// client, so the CommandLogging.OnWrite patch and the admin.audit normalizer can be verified
|
||||
/// end-to-end from a stub sidecar.
|
||||
///
|
||||
/// - Broadcast is a *genuine* trigger: CommandSystem.Handle runs [bcast, which raises
|
||||
/// EventSink.Command exactly as a staff keystroke would.
|
||||
/// - Ban/kick can't complete headlessly (they arm a target cursor with no client to click),
|
||||
/// so we call CommandLogging.WriteLine with the stock KickCommand line format — the same
|
||||
/// call that command makes at Commands.cs:1211, which is the point we tap.
|
||||
/// - A non-moderation log line confirms the normalizer ignores everything else.
|
||||
///
|
||||
/// Test scaffolding. Never deployed. Gated behind Bridge.AuditProbeOnStart (absent in a
|
||||
/// shipped Bridge.cfg, so Config.Get returns false and it never runs in production).
|
||||
/// </summary>
|
||||
public static class BridgeAuditProbe
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
if (Config.Get("Bridge.AuditProbeOnStart", false))
|
||||
EventSink.ServerStarted += () => Timer.DelayCall(TimeSpan.FromSeconds(4.0), Run);
|
||||
}
|
||||
|
||||
private static void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
var staffAcct = Accounting.Accounts.GetAccount("whitlocktech") as Account;
|
||||
var targetAcct = Accounting.Accounts.GetAccount("seed_010") as Account;
|
||||
|
||||
var from = staffAcct == null ? null : staffAcct[0];
|
||||
var target = targetAcct == null ? null : targetAcct[0];
|
||||
|
||||
if (from == null || target == null)
|
||||
{
|
||||
Console.WriteLine("[AuditProbe] need whitlocktech + seed_010 chars; seed the world first");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("[AuditProbe] genuine broadcast via [bcast ...");
|
||||
CommandSystem.Handle(from, CommandSystem.Prefix + "bcast in-game audit probe");
|
||||
|
||||
Console.WriteLine("[AuditProbe] simulating a resolved ban log line ...");
|
||||
CommandLogging.WriteLine(from, "{0} {1} {2} {3}",
|
||||
from.AccessLevel, CommandLogging.Format(from), "banning", CommandLogging.Format(target));
|
||||
|
||||
Console.WriteLine("[AuditProbe] simulating a resolved kick log line ...");
|
||||
CommandLogging.WriteLine(from, "{0} {1} {2} {3}",
|
||||
from.AccessLevel, CommandLogging.Format(from), "kicking", CommandLogging.Format(target));
|
||||
|
||||
Console.WriteLine("[AuditProbe] a non-moderation line (should be ignored) ...");
|
||||
CommandLogging.WriteLine(from, "{0} {1} used command '{2}'",
|
||||
from.AccessLevel, CommandLogging.Format(from), "Go 1 1 0");
|
||||
|
||||
Console.WriteLine("[AuditProbe] done");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("[AuditProbe] FAILED: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user