fix: daily report survives empty quser sessions (2.0.18-SAC)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-06-04 09:32:43 +10:00
parent 6384a45395
commit e3149785dc
2 changed files with 59 additions and 36 deletions
+58 -35
View File
@@ -89,7 +89,7 @@ $script:SkipLogDetailLimit = 15
# строки ниже, если правки «мелкие» и вы не хотите менять отображаемую версию в логах).
# Рекомендация: при значимых релизах меняйте и $ScriptVersion, и version.txt одинаково; при только
# исправлениях на шаре — достаточно поднять patch в version.txt (например 1.3.0.1).
$ScriptVersion = "2.0.17-SAC"
$ScriptVersion = "2.0.18-SAC"
# Логи (все под InstallRoot)
$LogFile = Join-Path $script:InstallRoot "Logs\login_monitor.log"
@@ -1831,23 +1831,49 @@ function Expand-DailyReportActiveUserEntries {
return ,@($out)
}
function Test-QuserEmptySessionsMessage {
param([string]$Line)
if ([string]::IsNullOrWhiteSpace($Line)) { return $false }
$t = $Line.Trim()
if ($t -match '(?i)No user(s)? exist(s)? for \*') { return $true }
if ($t -match '(?i)Не существуют пользователи для \*') { return $true }
if ($t -match '(?i)Пользователи отсутствуют') { return $true }
return $false
}
function Get-DailyReportActiveUsersFromQuser {
$lines = [System.Collections.Generic.List[string]]::new()
$quserExe = Join-Path $env:SystemRoot 'System32\quser.exe'
if (-not (Test-Path -LiteralPath $quserExe)) {
return ,$lines
}
$quserOutput = @(& $quserExe 2>$null)
if (-not $quserOutput -or $quserOutput.Count -le 1) {
$prevEa = $ErrorActionPreference
$quserOutput = @()
try {
$ErrorActionPreference = 'SilentlyContinue'
# quser пишет «No users exist for *» в stderr; при $ErrorActionPreference Stop это рвало отчёт.
$quserOutput = @(& $quserExe 2>&1)
} catch {
Write-Log "Daily report: quser failed (ignored): $($_.Exception.Message)"
return ,$lines
} finally {
$ErrorActionPreference = $prevEa
}
$dataLines = @(
$quserOutput | ForEach-Object { [string]$_ } | Where-Object {
-not [string]::IsNullOrWhiteSpace($_) -and -not (Test-QuserEmptySessionsMessage -Line $_)
}
)
if ($dataLines.Count -le 1) {
return ,$lines
}
foreach ($raw in @($quserOutput | Select-Object -Skip 1)) {
foreach ($raw in @($dataLines | Select-Object -Skip 1)) {
$line = ($raw -replace '\s+', ' ').Trim()
if ([string]::IsNullOrWhiteSpace($line)) { continue }
$parts = $line -split ' ', 2
if ($parts.Count -lt 1) { continue }
$u = $parts[0].Trim()
if ([string]::IsNullOrWhiteSpace($u) -or $u -eq 'USERNAME') { continue }
$u = $parts[0].Trim().TrimStart('>')
if ([string]::IsNullOrWhiteSpace($u) -or $u -eq 'USERNAME' -or $u -eq 'ИМЯ') { continue }
$lines.Add(('👤 {0}' -f $u)) | Out-Null
}
return ,$lines
@@ -1888,7 +1914,7 @@ function Build-DailyReportPlainBodyWindows {
if ($userCount -gt 0) {
foreach ($u in $ActiveUsers) { [void]$sb.AppendLine(" $u") }
} else {
[void]$sb.AppendLine(' (нет данных)')
[void]$sb.AppendLine(' (нет активных пользователей / RDP-сессий)')
}
[void]$sb.AppendLine('')
[void]$sb.AppendLine((Get-AgentNotificationSourcePlainLine))
@@ -3321,8 +3347,8 @@ function Format-RDGatewayEvent {
}
function Send-DailyReport {
try {
$reportTime = Get-Date
try {
$stats = Get-DailyReportAuthStats24h
$activeUsers = Expand-DailyReportActiveUserEntries -Entries @(Get-DailyReportActiveUsersFromQuser)
$plainBody = Build-DailyReportPlainBodyWindows -Stats $stats -ActiveUsers $activeUsers -ReportTime $reportTime
@@ -3360,7 +3386,7 @@ function Send-DailyReport {
-SacSummary "RDP 24ч: успех $($stats.rdp_success), неудач $($stats.rdp_failed), банов $($stats.active_bans)" `
-SacDetails $rdpReportDetails | Out-Null
Write-TextFileUtf8Bom -Path $LastReportFile -Text ($reportTime.ToString('yyyy-MM-dd HH:mm:ss'))
Write-Log "Ежедневный отчет отправлен"
Write-Log "Ежедневный отчет отправлен (активных сессий: $($activeUsers.Count))"
return $true
} catch {
Write-Log "Ошибка ежедневного отчета: $($_.Exception.Message)"
@@ -3395,8 +3421,21 @@ function Read-LastDailyReportSentDate {
}
}
function Try-ClaimDailyReportSlotToday {
param([datetime]$Now)
function Check-AndSendDailyReport {
if (-not (Test-DailyReportEnabledFlag)) {
return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute)
}
$now = Get-Date
$reportSlotToday = Get-Date -Year $now.Year -Month $now.Month -Day $now.Day -Hour $DailyReportHour -Minute $DailyReportMinute -Second 0
if ($now -lt $reportSlotToday) {
return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute)
}
$lastDate = Read-LastDailyReportSentDate
if ($null -ne $lastDate -and $lastDate -ge $now.Date) {
return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute)
}
$lockPath = Join-Path $script:InstallRoot 'Logs\.daily_report_claim.lock'
$lockStream = $null
@@ -3409,41 +3448,25 @@ function Try-ClaimDailyReportSlotToday {
)
} catch {
Write-Log 'Ежедневный отчёт: другой процесс уже отправляет (lock).'
return $false
return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute)
}
try {
$lastDate = Read-LastDailyReportSentDate
if ($null -ne $lastDate -and $lastDate -ge $Now.Date) {
return $false
if ($null -ne $lastDate -and $lastDate -ge $now.Date) {
return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute)
}
Write-TextFileUtf8Bom -Path $LastReportFile -Text ($Now.ToString('yyyy-MM-dd'))
return $true
if (-not (Send-DailyReport)) {
Write-Log 'Ежедневный отчёт: отправка не удалась, повтор на следующем цикле мониторинга.'
}
} catch {
Write-Log "Ежедневный отчёт: lock/send error: $($_.Exception.Message)"
} finally {
if ($null -ne $lockStream) {
$lockStream.Dispose()
}
Remove-Item -LiteralPath $lockPath -Force -ErrorAction SilentlyContinue
}
}
function Check-AndSendDailyReport {
if (-not (Test-DailyReportEnabledFlag)) {
return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute)
}
$now = Get-Date
$reportSlotToday = Get-Date -Year $now.Year -Month $now.Month -Day $now.Day -Hour $DailyReportHour -Minute $DailyReportMinute -Second 0
$shouldSend = $false
if ($now -ge $reportSlotToday) {
$lastDate = Read-LastDailyReportSentDate
if ($null -eq $lastDate -or $lastDate -lt $now.Date) {
$shouldSend = $true
}
}
if ($shouldSend -and (Try-ClaimDailyReportSlotToday -Now $now)) {
Send-DailyReport | Out-Null
}
return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute)
}
+1 -1
View File
@@ -1 +1 @@
2.0.17-SAC
2.0.18-SAC