Fix moderation history time handling

This commit is contained in:
Meta Vens 2026-05-05 11:54:01 +01:00
commit f9ff9d2cc9
5 changed files with 169 additions and 14 deletions

View file

@ -201,16 +201,33 @@ function readArchiveStore(archivePath) {
}
function filterMessages(messages, options) {
const beforeMs = options.before !== undefined ? parseTimestampToMs(options.before) : undefined;
const afterMs = options.after !== undefined ? parseTimestampToMs(options.after) : undefined;
return messages
.filter((message) => !message.deletedAt)
.filter((message) =>
options.includeBotPrompts ? true : readMessageKind(message) !== "bot_prompt",
)
.filter((message) => (options.channelId ? message.channelId === options.channelId : true))
.filter((message) => (options.before ? message.timestamp < options.before : true))
.filter((message) => (options.after ? message.timestamp > options.after : true))
.filter((message) => {
if (beforeMs === undefined) {
return true;
}
const messageMs = parseTimestampToMs(message.timestamp);
return messageMs !== undefined && messageMs < beforeMs;
})
.filter((message) => {
if (afterMs === undefined) {
return true;
}
const messageMs = parseTimestampToMs(message.timestamp);
return messageMs !== undefined && messageMs > afterMs;
})
.filter((message) => normalizeContent(message.content).length >= (options.minContentLength ?? 0))
.sort((left, right) => left.timestamp.localeCompare(right.timestamp));
.sort(compareMessagesByTimestamp);
}
function normalizeContent(content) {
@ -269,6 +286,30 @@ function hashSeed(seed) {
return hash >>> 0;
}
function compareMessagesByTimestamp(left, right) {
const leftMs = parseTimestampToMs(left.timestamp);
const rightMs = parseTimestampToMs(right.timestamp);
if (leftMs !== undefined && rightMs !== undefined && leftMs !== rightMs) {
return leftMs - rightMs;
}
if (left.timestamp !== right.timestamp) {
return left.timestamp.localeCompare(right.timestamp);
}
return String(left.id ?? "").localeCompare(String(right.id ?? ""));
}
function parseTimestampToMs(value) {
if (typeof value !== "string" || value.length === 0) {
return undefined;
}
const parsed = Date.parse(value);
return Number.isNaN(parsed) ? undefined : parsed;
}
function stripLeadingBom(input) {
return input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;
}

View file

@ -158,11 +158,12 @@ function readArchiveStore(archivePath) {
}
function filterMessages(messages, options) {
const lowerBound =
options.after ??
(options.hours !== undefined
? new Date(Date.now() - options.hours * 60 * 60 * 1000).toISOString()
: undefined);
const lowerBoundMs =
options.after !== undefined
? parseTimestampToMs(options.after)
: options.hours !== undefined
? Date.now() - options.hours * 60 * 60 * 1000
: undefined;
return messages
.filter((message) => !message.deletedAt)
@ -170,8 +171,15 @@ function filterMessages(messages, options) {
options.includeBotPrompts ? true : readMessageKind(message) !== "bot_prompt",
)
.filter((message) => (options.channelId ? message.channelId === options.channelId : true))
.filter((message) => (lowerBound ? message.timestamp > lowerBound : true))
.sort((left, right) => left.timestamp.localeCompare(right.timestamp));
.filter((message) => {
if (lowerBoundMs === undefined) {
return true;
}
const messageMs = parseTimestampToMs(message.timestamp);
return messageMs !== undefined && messageMs > lowerBoundMs;
})
.sort(compareMessagesByTimestamp);
}
function readMessageKind(message) {
@ -201,6 +209,30 @@ function formatTranscriptLine(message) {
return `[${message.timestamp}] ${message.authorName} (${message.authorId}) channel=${message.channelId}${threadSuffix}: ${message.content}${attachmentSuffix}`;
}
function compareMessagesByTimestamp(left, right) {
const leftMs = parseTimestampToMs(left.timestamp);
const rightMs = parseTimestampToMs(right.timestamp);
if (leftMs !== undefined && rightMs !== undefined && leftMs !== rightMs) {
return leftMs - rightMs;
}
if (left.timestamp !== right.timestamp) {
return left.timestamp.localeCompare(right.timestamp);
}
return String(left.id ?? "").localeCompare(String(right.id ?? ""));
}
function parseTimestampToMs(value) {
if (typeof value !== "string" || value.length === 0) {
return undefined;
}
const parsed = Date.parse(value);
return Number.isNaN(parsed) ? undefined : parsed;
}
function stripLeadingBom(input) {
return input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;
}

View file

@ -68,7 +68,9 @@ function Write-JsonFile {
New-Item -ItemType Directory -Force -Path $directory | Out-Null
}
$Data | ConvertTo-Json -Depth 12 | Set-Content -Path $Path -Encoding UTF8
$json = $Data | ConvertTo-Json -Depth 12
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
[System.IO.File]::WriteAllText($Path, $json, $utf8NoBom)
}
function Append-RunLog {
@ -94,6 +96,46 @@ function Ensure-ModerationState {
Copy-Item -Path $stateTemplatePath -Destination $stateFilePath -Force
}
function Read-JsonFile {
param(
[Parameter(Mandatory = $true)]
[string] $Path
)
if (-not (Test-Path $Path)) {
return $null
}
$raw = Get-Content -Path $Path -Raw -Encoding UTF8
if ([string]::IsNullOrWhiteSpace($raw)) {
return $null
}
return $raw | ConvertFrom-Json
}
function Set-ModerationCursor {
param(
[Parameter(Mandatory = $true)]
[string] $Path,
[AllowNull()] $Cursor
)
$state = Read-JsonFile -Path $Path
if ($null -eq $state) {
return
}
if ($null -eq $state.moderation_runtime) {
$state | Add-Member -NotePropertyName moderation_runtime -NotePropertyValue ([pscustomobject]@{}) -Force
}
$state.moderation_runtime.cursor = $Cursor
Write-JsonFile -Path $Path -Data $state
}
function Split-CommandArgs {
param(
[string] $Value
@ -227,6 +269,36 @@ if (-not (Test-Path $mcpServerPath)) {
throw "Missing built MCP server at $mcpServerPath. Run npm run build first."
}
$preRunState = Read-JsonFile -Path $stateFilePath
$priorCursor = if ($null -ne $preRunState -and $null -ne $preRunState.moderation_runtime) {
$preRunState.moderation_runtime.cursor
} else {
$null
}
$historyArgs = @("scripts/export-recent-discord-history.mjs")
if ($null -ne $priorCursor -and -not [string]::IsNullOrWhiteSpace($priorCursor.lastReviewedTimestamp)) {
$historyArgs += @("--after", [string]$priorCursor.lastReviewedTimestamp, "--limit", "120")
} else {
$historyArgs += @("--hours", "6", "--limit", "120")
}
$preRunHistoryJson = & node @historyArgs
if ($LASTEXITCODE -ne 0) {
throw "Failed to poll recent Discord history before moderation run."
}
$preRunHistory = $preRunHistoryJson | ConvertFrom-Json
$observedCursor = $priorCursor
if ($null -ne $preRunHistory.messages -and $preRunHistory.messages.Count -gt 0) {
$latestObservedMessage = $preRunHistory.messages[$preRunHistory.messages.Count - 1]
$observedCursor = [pscustomobject]@{
lastReviewedMessageId = $latestObservedMessage.id
lastReviewedTimestamp = $latestObservedMessage.timestamp
}
}
Write-JsonFile -Path $lockPath -Data @{
pid = $PID
startedAt = $startedAtUtc.ToString("o")
@ -328,7 +400,14 @@ try {
$combinedText | Set-Content -Path $tracePath -Encoding UTF8
$finishedAtUtc = [DateTime]::UtcNow
$stateUpdated = $false
if ($exitCode -eq 0) {
Set-ModerationCursor -Path $stateFilePath -Cursor $observedCursor
}
$stateWriteAfter = (Get-Item $stateFilePath).LastWriteTimeUtc
$stateUpdated = ($stateWriteAfter -gt $stateWriteBefore)
$lastMessage = if (Test-Path $lastMessagePath) { Get-Content -Path $lastMessagePath -Raw } else { "" }
Write-JsonFile -Path $statusPath -Data @{
@ -342,13 +421,13 @@ Write-JsonFile -Path $statusPath -Data @{
stateFile = $stateFilePath
stateWriteBeforeUtc = $stateWriteBefore.ToString("o")
stateWriteAfterUtc = $stateWriteAfter.ToString("o")
stateUpdated = ($stateWriteAfter -gt $stateWriteBefore)
stateUpdated = $stateUpdated
tracePath = $tracePath
lastMessagePath = $lastMessagePath
lastMessage = $lastMessage.Trim()
}
Append-RunLog ("exit={0} stateUpdated={1} summary={2}" -f $exitCode, ($stateWriteAfter -gt $stateWriteBefore), ($lastMessage.Trim()))
Append-RunLog ("exit={0} stateUpdated={1} summary={2}" -f $exitCode, $stateUpdated, ($lastMessage.Trim()))
Remove-Item -Path $lockPath -Force -ErrorAction SilentlyContinue

View file

@ -23,7 +23,9 @@ function Write-JsonFile {
New-Item -ItemType Directory -Force -Path $directory | Out-Null
}
$Data | ConvertTo-Json -Depth 10 | Set-Content -Path $Path -Encoding UTF8
$json = $Data | ConvertTo-Json -Depth 10
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
[System.IO.File]::WriteAllText($Path, $json, $utf8NoBom)
}
function Append-RunLog {