# Collects everything needed to diagnose "it isn't working" into one text file # on the Desktop. Passwords are masked. Called by report.bat. $scriptRoot = Split-Path -Parent $PSCommandPath $stamp = Get-Date -Format "yyyyMMdd-HHmm" $desktop = [Environment]::GetFolderPath("Desktop") if (-not $desktop) { $desktop = $scriptRoot } $outFile = Join-Path $desktop "MelissaUploader-Report-$stamp.txt" $lines = New-Object System.Collections.ArrayList function Add-Line($text) { [void]$lines.Add([string]$text) } function Add-Head($text) { Add-Line "" Add-Line ("=" * 60) Add-Line " $text" Add-Line ("=" * 60) } Add-Line "Melissa Uploader report" Add-Line "Taken : $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Add-Line "PC : $env:COMPUTERNAME" Add-Line "User : $env:USERNAME" Add-Line "Folder : $scriptRoot" Add-Line "Windows : $([Environment]::OSVersion.VersionString)" Add-Line "PowerShell: $($PSVersionTable.PSVersion)" # -- Which version of the tool is installed ----------------------------------- Add-Head "Version" $versionFile = Join-Path $scriptRoot "VERSION.txt" if (Test-Path $versionFile) { Add-Line "VERSION.txt: $((Get-Content $versionFile -Raw).Trim())" } else { Add-Line "VERSION.txt: MISSING" Add-Line ">> This copy is older than 2026-08-26. It does not have the setup" Add-Line ">> wizard. Whoever maintains this needs to copy the new files over." } # -- Which files are present -------------------------------------------------- Add-Head "Files in the folder" $expected = @("engine.ps1", "run-all.bat", "run-all.ps1", "run.bat", "setup.bat", "setup-entity.bat", "Setup-Entity.ps1", "Get-WinSCP.ps1", "Get-Report.ps1") foreach ($name in $expected) { $path = Join-Path $scriptRoot $name if (Test-Path $path) { $item = Get-Item $path Add-Line (" {0,-20} {1} {2,8} bytes" -f $name, $item.LastWriteTime.ToString("yyyy-MM-dd HH:mm"), $item.Length) } else { Add-Line (" {0,-20} MISSING" -f $name) } } # -- WinSCP ------------------------------------------------------------------- Add-Head "WinSCP" $winscpDll = Join-Path $scriptRoot "WinSCP\WinSCPnet.dll" if (-not (Test-Path $winscpDll)) { Add-Line "WinSCPnet.dll MISSING - setup.bat has not been run (or it failed)." } else { $dll = Get-Item $winscpDll Add-Line "WinSCPnet.dll found: $($dll.Length) bytes, $($dll.LastWriteTime)" $zone = Get-Item $winscpDll -Stream Zone.Identifier -ErrorAction SilentlyContinue if ($zone) { Add-Line "BLOCKED by Windows (Mark of the Web) - engine.ps1 unblocks this itself on every run." } else { Add-Line "Not blocked." } try { Add-Type -Path $winscpDll -ErrorAction Stop Add-Line "Loads OK." } catch { Add-Line "WILL NOT LOAD: $($_.Exception.Message)" } } # -- Entities ----------------------------------------------------------------- Add-Head "Entities" $entitiesDir = Join-Path $scriptRoot "Entities" if (-not (Test-Path $entitiesDir)) { Add-Line "No Entities\ folder at all." } else { $entities = @(Get-ChildItem -Path $entitiesDir -Directory | Where-Object { $_.Name -ne "EXAMPLE" }) if ($entities.Count -eq 0) { Add-Line "Entities\ exists but is empty." } foreach ($entity in $entities) { Add-Line "" Add-Line ("-- $($entity.Name) " + ("-" * 40)) $configFile = Join-Path $entity.FullName "entity.json" if (-not (Test-Path $configFile)) { Add-Line " entity.json MISSING - this entity is skipped by run-all." } else { $raw = Get-Content $configFile -Raw # Mask anything that looks like a password before this leaves the PC. $masked = $raw -replace '("password"\s*:\s*")[^"]*(")', '$1********$2' Add-Line " entity.json:" foreach ($l in ($masked -split "`r?`n")) { Add-Line " $l" } try { $raw | ConvertFrom-Json | Out-Null Add-Line " Reads as valid JSON." } catch { Add-Line " *** THIS FILE IS NOT VALID JSON *** $($_.Exception.Message)" } } $export1 = Join-Path $entity.FullName "Export1" if (-not (Test-Path $export1)) { Add-Line " No Export1\ folder." continue } $today = Get-Date -Format "yyyyMMdd" foreach ($practice in (Get-ChildItem -Path $export1 -Directory)) { $dateDir = Join-Path $practice.FullName $today if (Test-Path $dateDir) { $files = @(Get-ChildItem -Path $dateDir -File) $byExt = ($files | Group-Object Extension | ForEach-Object { "$($_.Count)x $($_.Name)" }) -join ", " if (-not $byExt) { $byExt = "empty" } Add-Line " $($practice.Name): $today\ has $($files.Count) file(s) - $byExt" } else { Add-Line " $($practice.Name): no $today\ folder" } $pj = Join-Path $practice.FullName "practice.json" if (Test-Path $pj) { $pmasked = (Get-Content $pj -Raw) -replace '("password"\s*:\s*")[^"]*(")', '$1********$2' Add-Line " practice.json: $($pmasked -replace '\s+', ' ')" } } } } # -- Logs --------------------------------------------------------------------- Add-Head "Most recent log" $logsDir = Join-Path $scriptRoot "Logs" if (-not (Test-Path $logsDir)) { Add-Line "No Logs\ folder - engine.ps1 has never completed a run here." } else { $log = Get-ChildItem -Path $logsDir -Filter "*.log" | Where-Object { $_.Name -notmatch "winscp_debug" } | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if (-not $log) { Add-Line "Logs\ is empty." } else { Add-Line "$($log.Name) (last written $($log.LastWriteTime))" Add-Line "" $content = Get-Content $log.FullName -Tail 120 foreach ($l in $content) { Add-Line " $l" } } } Add-Head "End of report" # -- Write it out ------------------------------------------------------------- $text = $lines -join "`r`n" try { Set-Content -Path $outFile -Value $text -Encoding UTF8 $saved = $true } catch { $outFile = Join-Path $scriptRoot "MelissaUploader-Report-$stamp.txt" Set-Content -Path $outFile -Value $text -Encoding UTF8 $saved = $true } Write-Host $text Write-Host "" Write-Host ("=" * 60) -ForegroundColor Cyan if ($saved) { Write-Host "Report saved to:" -ForegroundColor Green Write-Host " $outFile" -ForegroundColor Green Write-Host "" Write-Host "Send that file to Brad - it has everything needed to work out" -ForegroundColor Cyan Write-Host "what went wrong. Passwords are replaced with ******** in it." -ForegroundColor Cyan } Write-Host ("=" * 60) -ForegroundColor Cyan