fix(print): detect generic doc names (Печать документа) + integrate email collector into deploy

Print document naming:
- Add Test-IsGenericDocumentName() to detect placeholder names like
  'Печать документа', 'Print Document', 'Remote Downlevel Document', etc.
- Add Test-NeedsBetterDocumentName() that combines mojibake, generic and
  numeric-only checks into a single gate for PrintService event log fallback
- Replace Test-LooksLikeMojibakeQuestionMarks with Test-NeedsBetterDocumentName
  in Win32_PrintJob polling and Get-PrintServiceDocumentFallback

Email collector deployment integration:
- Ansible: add email-outbound-collector.ps1 to deploy_aw_windows.yml file list
- Ansible: add aw_windows_email_collector_enabled/mode vars
- InnoSetup: add email-outbound-collector.ps1 to [Files] section
- Common module: add EmailCollectorScript to Copy-ActivityWatchCollectorAssets,
  New-ActivityWatchDeploymentConfig, launch-watchers script
- Deploy scripts: pass EmailCollectorScript through pipeline
- Deployment configs: add emailCollectorScript path
- Sync all changes to install-kit copy

Co-Authored-By: Fashion Lisa <igor04091968@gmail.com>
This commit is contained in:
Devin AI
2026-05-04 18:07:14 +00:00
co-authored by Fashion Lisa
parent bfee99d679
commit a35a8a51bd
19 changed files with 383 additions and 52 deletions
+14
View File
@@ -269,6 +269,7 @@ function Copy-ActivityWatchCollectorAssets {
[string]$FileCollectorScriptSource,
[Parameter(Mandatory = $true)]
[string]$SessionCollectorScriptSource,
[string]$EmailCollectorScriptSource,
[Parameter(Mandatory = $true)]
[string]$ExampleRulesSource,
[Parameter(Mandatory = $true)]
@@ -285,6 +286,7 @@ function Copy-ActivityWatchCollectorAssets {
$endpointCollectorTarget = Join-Path $StateRoot 'dlp-endpoint-signals-collector.ps1'
$fileCollectorTarget = Join-Path $StateRoot 'file-operations-collector.ps1'
$sessionCollectorTarget = Join-Path $StateRoot 'worktime-session-collector.ps1'
$emailCollectorTarget = Join-Path $StateRoot 'email-outbound-collector.ps1'
$exampleRulesTarget = Join-Path $StateRoot 'web-category-rules.example.json'
$rulesTarget = Join-Path $StateRoot 'web-category-rules.json'
$examplePolicyTarget = Join-Path $StateRoot 'dlp-policy.example.json'
@@ -294,6 +296,9 @@ function Copy-ActivityWatchCollectorAssets {
Copy-Item -LiteralPath $EndpointCollectorScriptSource -Destination $endpointCollectorTarget -Force
Copy-Item -LiteralPath $FileCollectorScriptSource -Destination $fileCollectorTarget -Force
Copy-Item -LiteralPath $SessionCollectorScriptSource -Destination $sessionCollectorTarget -Force
if ($EmailCollectorScriptSource -and (Test-Path -LiteralPath $EmailCollectorScriptSource)) {
Copy-Item -LiteralPath $EmailCollectorScriptSource -Destination $emailCollectorTarget -Force
}
Copy-Item -LiteralPath $ExampleRulesSource -Destination $exampleRulesTarget -Force
Copy-Item -LiteralPath $ExamplePolicySource -Destination $examplePolicyTarget -Force
@@ -315,6 +320,7 @@ function Copy-ActivityWatchCollectorAssets {
EndpointCollectorScript = $endpointCollectorTarget
FileCollectorScript = $fileCollectorTarget
SessionCollectorScript = $sessionCollectorTarget
EmailCollectorScript = $emailCollectorTarget
ExampleRules = $exampleRulesTarget
ActiveRules = $rulesTarget
ExamplePolicy = $examplePolicyTarget
@@ -344,6 +350,7 @@ function New-ActivityWatchDeploymentConfig {
[string]$FileCollectorScript,
[Parameter(Mandatory = $true)]
[string]$SessionCollectorScript,
[string]$EmailCollectorScript,
[Parameter(Mandatory = $true)]
[string]$RulesPath,
[Parameter(Mandatory = $true)]
@@ -387,6 +394,7 @@ function New-ActivityWatchDeploymentConfig {
logsRoot = $LogsRoot
collectorScript = $CollectorScript
endpointCollectorScript = $EndpointCollectorScript
emailCollectorScript = $EmailCollectorScript
fileCollectorScript = $FileCollectorScript
sessionCollectorScript = $SessionCollectorScript
rulesPath = $RulesPath
@@ -402,6 +410,7 @@ function New-ActivityWatchDeploymentConfig {
afkEnabled = $AfkEnabled
windowEnabled = $WindowEnabled
fileOpsEnabled = $FileOpsEnabled
emailEnabled = ($null -ne $EmailCollectorScript -and $EmailCollectorScript -ne '')
}
logging = [pscustomobject]@{
localAgentLogsEnabled = $LocalAgentLogsEnabled
@@ -749,6 +758,8 @@ function Start-CollectorScriptIfNeeded {
`$afkEnabled = if (`$config.PSObject.Properties.Name -contains 'collectors' -and `$config.collectors.PSObject.Properties.Name -contains 'afkEnabled') { [bool]`$config.collectors.afkEnabled } else { `$true }
`$windowEnabled = if (`$config.PSObject.Properties.Name -contains 'collectors' -and `$config.collectors.PSObject.Properties.Name -contains 'windowEnabled') { [bool]`$config.collectors.windowEnabled } else { `$true }
`$fileOpsEnabled = if (`$config.PSObject.Properties.Name -contains 'collectors' -and `$config.collectors.PSObject.Properties.Name -contains 'fileOpsEnabled') { [bool]`$config.collectors.fileOpsEnabled } else { `$true }
`$emailEnabled = if (`$config.PSObject.Properties.Name -contains 'collectors' -and `$config.collectors.PSObject.Properties.Name -contains 'emailEnabled') { [bool]`$config.collectors.emailEnabled } else { `$false }
`$emailCollectorScript = if (`$config.paths.PSObject.Properties.Name -contains 'emailCollectorScript') { [string]`$config.paths.emailCollectorScript } else { Join-Path `$stateRoot 'email-outbound-collector.ps1' }
`$launchLockPath = New-LaunchLock -StateRoot `$stateRoot -SessionId `$sessionId
if (-not `$launchLockPath) {
return
@@ -782,6 +793,9 @@ try {
Start-CollectorScriptIfNeeded -ScriptPath `$fileCollectorScript -ConfigPath `$ConfigPath -PowerShellExe `$powershellExe -SessionId `$sessionId
}
Start-CollectorScriptIfNeeded -ScriptPath `$sessionCollectorScript -ConfigPath `$ConfigPath -PowerShellExe `$powershellExe -SessionId `$sessionId
if (`$emailEnabled -and (Test-Path -LiteralPath `$emailCollectorScript)) {
Start-CollectorScriptIfNeeded -ScriptPath `$emailCollectorScript -ConfigPath `$ConfigPath -PowerShellExe `$powershellExe -SessionId `$sessionId
}
}
finally {
if (`$launchLockPath -and (Test-Path -LiteralPath `$launchLockPath)) {
+3
View File
@@ -45,6 +45,7 @@ $launchScriptPath = Join-Path $StateRoot 'launch-watchers.ps1'
$recoveryScriptPath = Join-Path $StateRoot 'recovery-loop.ps1'
$collectorSource = Join-Path $PSScriptRoot 'browser-domains-native-collector.ps1'
$endpointCollectorSource = Join-Path $PSScriptRoot 'dlp-endpoint-signals-collector.ps1'
$emailCollectorSource = Join-Path $PSScriptRoot 'email-outbound-collector.ps1'
$fileCollectorSource = Join-Path $PSScriptRoot 'file-operations-collector.ps1'
$sessionCollectorSource = Join-Path $PSScriptRoot 'worktime-session-collector.ps1'
$exampleRulesSource = Join-Path $PSScriptRoot 'web-category-rules.example.json'
@@ -61,6 +62,7 @@ Get-ActivityWatchExecutableMap -InstallRoot $InstallRoot | Out-Null
$assetResult = Copy-ActivityWatchCollectorAssets `
-CollectorScriptSource $collectorSource `
-EndpointCollectorScriptSource $endpointCollectorSource `
-EmailCollectorScriptSource $emailCollectorSource `
-FileCollectorScriptSource $fileCollectorSource `
-SessionCollectorScriptSource $sessionCollectorSource `
-ExampleRulesSource $exampleRulesSource `
@@ -82,6 +84,7 @@ $config = New-ActivityWatchDeploymentConfig `
-LogsRoot $logsRoot `
-CollectorScript $assetResult.CollectorScript `
-EndpointCollectorScript $assetResult.EndpointCollectorScript `
-EmailCollectorScript $assetResult.EmailCollectorScript `
-FileCollectorScript $assetResult.FileCollectorScript `
-SessionCollectorScript $assetResult.SessionCollectorScript `
-RulesPath $assetResult.ActiveRules `
+3
View File
@@ -42,6 +42,7 @@ $launchScriptPath = Join-Path $StateRoot 'launch-watchers.ps1'
$recoveryScriptPath = Join-Path $StateRoot 'recovery-loop.ps1'
$collectorSource = Join-Path $PSScriptRoot 'browser-domains-native-collector.ps1'
$endpointCollectorSource = Join-Path $PSScriptRoot 'dlp-endpoint-signals-collector.ps1'
$emailCollectorSource = Join-Path $PSScriptRoot 'email-outbound-collector.ps1'
$sessionCollectorSource = Join-Path $PSScriptRoot 'worktime-session-collector.ps1'
$exampleRulesSource = Join-Path $PSScriptRoot 'web-category-rules.example.json'
$examplePolicySource = Join-Path $PSScriptRoot 'dlp-policy.example.json'
@@ -56,6 +57,7 @@ Get-ActivityWatchExecutableMap -InstallRoot $InstallRoot | Out-Null
$assetResult = Copy-ActivityWatchCollectorAssets `
-CollectorScriptSource $collectorSource `
-EndpointCollectorScriptSource $endpointCollectorSource `
-EmailCollectorScriptSource $emailCollectorSource `
-SessionCollectorScriptSource $sessionCollectorSource `
-ExampleRulesSource $exampleRulesSource `
-ExamplePolicySource $examplePolicySource `
@@ -76,6 +78,7 @@ $config = New-ActivityWatchDeploymentConfig `
-LogsRoot $logsRoot `
-CollectorScript $assetResult.CollectorScript `
-EndpointCollectorScript $assetResult.EndpointCollectorScript `
-EmailCollectorScript $assetResult.EmailCollectorScript `
-SessionCollectorScript $assetResult.SessionCollectorScript `
-RulesPath $assetResult.ActiveRules `
-PolicyPath $assetResult.ActivePolicy `
+32 -7
View File
@@ -516,6 +516,35 @@ function Test-LooksLikeMojibakeQuestionMarks {
return $Value -match '\?{2,}'
}
function Test-IsGenericDocumentName {
param([AllowNull()][string]$Value)
if ([string]::IsNullOrWhiteSpace($Value)) { return $true }
$generic = @(
'^\s*Печать документа\s*$',
'^\s*Print Document\s*$',
'^\s*Document\s*$',
'^\s*Документ\s*$',
'^\s*Remote Downlevel Document\s*$',
'^\s*Local Downlevel Document\s*$',
'^\s*Untitled\s*$',
'^\s*Без имени\s*$',
'^\s*Без названия\s*$'
)
foreach ($pattern in $generic) {
if ($Value -match $pattern) { return $true }
}
return $false
}
function Test-NeedsBetterDocumentName {
param([AllowNull()][string]$Value)
if ([string]::IsNullOrWhiteSpace($Value)) { return $true }
if (Test-LooksLikeMojibakeQuestionMarks -Value $Value) { return $true }
if (Test-IsGenericDocumentName -Value $Value) { return $true }
if ($Value -match '^[0-9]+$') { return $true }
return $false
}
function Normalize-OwnerForMatch {
param([AllowNull()][string]$Value)
if ([string]::IsNullOrWhiteSpace($Value)) { return '' }
@@ -602,7 +631,7 @@ function Get-PrintServiceDocumentFallback {
)
$preferred = [string]$EventSummary.DocumentName
if (-not (Test-LooksLikeMojibakeQuestionMarks -Value $preferred) -and $preferred -notmatch '^[0-9]+$') {
if (-not (Test-NeedsBetterDocumentName -Value $preferred)) {
return $preferred
}
@@ -615,17 +644,13 @@ function Get-PrintServiceDocumentFallback {
if ($candidate -eq $preferred) { continue }
if ($Owner -and $candidate -like "*$Owner*") { continue }
if ($PrinterName -and $candidate -like "*$PrinterName*") { continue }
if (Test-LooksLikeMojibakeQuestionMarks -Value $candidate) { continue }
if (Test-NeedsBetterDocumentName -Value $candidate) { continue }
if ($candidate -match '[\\/:]' -and $candidate -match '\.[A-Za-z0-9]{1,8}$') {
$pathCandidates.Add($candidate)
continue
}
if ($candidate -match '^[0-9]+$') {
continue
}
$textCandidates.Add($candidate)
}
@@ -828,7 +853,7 @@ while ($true) {
$owner = [string]$job.Owner
$documentNameOriginal = $documentName
if (Test-LooksLikeMojibakeQuestionMarks -Value $documentName) {
if (Test-NeedsBetterDocumentName -Value $documentName) {
$eventDocumentName = Get-BetterDocumentNameFromPrintServiceEvents -Owner $owner -PrinterName $printerName
if ($eventDocumentName) {
$documentName = $eventDocumentName
+2
View File
@@ -101,6 +101,7 @@ Get-ActivityWatchExecutableMap -InstallRoot $effectiveInstallRoot | Out-Null
$assetResult = Copy-ActivityWatchCollectorAssets `
-CollectorScriptSource (Join-Path $PSScriptRoot 'browser-domains-native-collector.ps1') `
-EndpointCollectorScriptSource (Join-Path $PSScriptRoot 'dlp-endpoint-signals-collector.ps1') `
-EmailCollectorScriptSource (Join-Path $PSScriptRoot 'email-outbound-collector.ps1') `
-FileCollectorScriptSource (Join-Path $PSScriptRoot 'file-operations-collector.ps1') `
-SessionCollectorScriptSource (Join-Path $PSScriptRoot 'worktime-session-collector.ps1') `
-ExampleRulesSource (Join-Path $PSScriptRoot 'web-category-rules.example.json') `
@@ -122,6 +123,7 @@ $config = New-ActivityWatchDeploymentConfig `
-LogsRoot $effectiveLogsRoot `
-CollectorScript $effectiveCollector `
-EndpointCollectorScript $effectiveEndpointCollector `
-EmailCollectorScript $assetResult.EmailCollectorScript `
-FileCollectorScript $effectiveFileCollector `
-SessionCollectorScript $effectiveSessionCollector `
-RulesPath $effectiveRules `
@@ -42,6 +42,7 @@ Source: "..\..\migrate-awatch-rus-paths.ps1"; DestDir: "{app}\windows"; Flags: i
Source: "..\..\worktime-session-collector.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion
Source: "..\..\browser-domains-native-collector.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion
Source: "..\..\dlp-endpoint-signals-collector.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion
Source: "..\..\email-outbound-collector.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion
Source: "..\..\web-category-rules.example.json"; DestDir: "{app}\windows"; Flags: ignoreversion
Source: "..\..\dlp-policy.example.json"; DestDir: "{app}\windows"; Flags: ignoreversion
; Offline payload (optional): place ZIP into windows/installkit/innosetup/payload/ before compiling.