Fix moderation history time handling
This commit is contained in:
parent
4f28ac0bff
commit
f9ff9d2cc9
5 changed files with 169 additions and 14 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue