mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 21:29:42 +00:00
443 lines
14 KiB
PowerShell
443 lines
14 KiB
PowerShell
# Mole - Application-Specific Cleanup Module
|
|
# Cleans leftover data from uninstalled apps and app-specific caches
|
|
|
|
#Requires -Version 5.1
|
|
Set-StrictMode -Version Latest
|
|
|
|
# Prevent multiple sourcing
|
|
if ((Get-Variable -Name 'MOLE_CLEAN_APPS_LOADED' -Scope Script -ErrorAction SilentlyContinue) -and $script:MOLE_CLEAN_APPS_LOADED) { return }
|
|
$script:MOLE_CLEAN_APPS_LOADED = $true
|
|
|
|
# Import dependencies
|
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
. "$scriptDir\..\core\base.ps1"
|
|
. "$scriptDir\..\core\log.ps1"
|
|
. "$scriptDir\..\core\file_ops.ps1"
|
|
|
|
# ============================================================================
|
|
# Orphaned App Data Detection
|
|
# ============================================================================
|
|
|
|
function Get-InstalledPrograms {
|
|
<#
|
|
.SYNOPSIS
|
|
Get list of installed programs from registry
|
|
#>
|
|
|
|
$programs = @()
|
|
|
|
$registryPaths = @(
|
|
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
|
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
|
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
|
)
|
|
|
|
foreach ($path in $registryPaths) {
|
|
$items = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.DisplayName } |
|
|
Select-Object DisplayName, InstallLocation, Publisher
|
|
if ($items) {
|
|
$programs += $items
|
|
}
|
|
}
|
|
|
|
# Also check UWP apps
|
|
try {
|
|
$uwpApps = Get-AppxPackage -ErrorAction SilentlyContinue |
|
|
Select-Object @{N='DisplayName';E={$_.Name}}, @{N='InstallLocation';E={$_.InstallLocation}}, Publisher
|
|
if ($uwpApps) {
|
|
$programs += $uwpApps
|
|
}
|
|
}
|
|
catch {
|
|
Write-Debug "Could not enumerate UWP apps: $_"
|
|
}
|
|
|
|
return $programs
|
|
}
|
|
|
|
function Find-OrphanedAppData {
|
|
<#
|
|
.SYNOPSIS
|
|
Find app data folders for apps that are no longer installed
|
|
#>
|
|
param([int]$DaysOld = 60)
|
|
|
|
$installedPrograms = Get-InstalledPrograms
|
|
$installedNames = $installedPrograms | ForEach-Object { $_.DisplayName.ToLower() }
|
|
|
|
$orphanedPaths = @()
|
|
$cutoffDate = (Get-Date).AddDays(-$DaysOld)
|
|
|
|
# Check common app data locations
|
|
$appDataPaths = @(
|
|
@{ Path = $env:APPDATA; Type = "Roaming" }
|
|
@{ Path = $env:LOCALAPPDATA; Type = "Local" }
|
|
)
|
|
|
|
foreach ($location in $appDataPaths) {
|
|
if (-not (Test-Path $location.Path)) { continue }
|
|
|
|
$folders = Get-ChildItem -Path $location.Path -Directory -ErrorAction SilentlyContinue
|
|
|
|
foreach ($folder in $folders) {
|
|
# Skip system folders
|
|
$skipFolders = @('Microsoft', 'Windows', 'Packages', 'Programs', 'Temp', 'Roaming')
|
|
if ($folder.Name -in $skipFolders) { continue }
|
|
|
|
# Skip if recently modified
|
|
if ($folder.LastWriteTime -gt $cutoffDate) { continue }
|
|
|
|
# Check if app is installed using stricter matching
|
|
# Require exact match or that folder name is a clear prefix/suffix of app name
|
|
$isInstalled = $false
|
|
$folderLower = $folder.Name.ToLower()
|
|
foreach ($name in $installedNames) {
|
|
# Exact match
|
|
if ($name -eq $folderLower) {
|
|
$isInstalled = $true
|
|
break
|
|
}
|
|
# Folder is prefix of app name (e.g., "chrome" matches "chrome browser")
|
|
if ($name.StartsWith($folderLower) -and $folderLower.Length -ge 4) {
|
|
$isInstalled = $true
|
|
break
|
|
}
|
|
# App name is prefix of folder (e.g., "vscode" matches "vscode-data")
|
|
if ($folderLower.StartsWith($name) -and $name.Length -ge 4) {
|
|
$isInstalled = $true
|
|
break
|
|
}
|
|
}
|
|
|
|
if (-not $isInstalled) {
|
|
$orphanedPaths += @{
|
|
Path = $folder.FullName
|
|
Name = $folder.Name
|
|
Type = $location.Type
|
|
Size = (Get-PathSize -Path $folder.FullName)
|
|
LastModified = $folder.LastWriteTime
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $orphanedPaths
|
|
}
|
|
|
|
function Clear-OrphanedAppData {
|
|
<#
|
|
.SYNOPSIS
|
|
Clean orphaned application data
|
|
#>
|
|
param([int]$DaysOld = 60)
|
|
|
|
Start-Section "Orphaned app data"
|
|
|
|
$orphaned = Find-OrphanedAppData -DaysOld $DaysOld
|
|
|
|
if ($orphaned.Count -eq 0) {
|
|
Write-Info "No orphaned app data found"
|
|
Stop-Section
|
|
return
|
|
}
|
|
|
|
# Filter by size (only clean if > 10MB to avoid noise)
|
|
$significantOrphans = $orphaned | Where-Object { $_.Size -gt 10MB }
|
|
|
|
if ($significantOrphans.Count -gt 0) {
|
|
$totalSize = ($significantOrphans | Measure-Object -Property Size -Sum).Sum
|
|
$sizeHuman = Format-ByteSize -Bytes $totalSize
|
|
|
|
Write-Info "Found $($significantOrphans.Count) orphaned folders ($sizeHuman)"
|
|
|
|
foreach ($orphan in $significantOrphans) {
|
|
$orphanSize = Format-ByteSize -Bytes $orphan.Size
|
|
Remove-SafeItem -Path $orphan.Path -Description "$($orphan.Name) ($orphanSize)" -Recurse
|
|
}
|
|
}
|
|
|
|
Stop-Section
|
|
}
|
|
|
|
# ============================================================================
|
|
# Specific Application Cleanup
|
|
# ============================================================================
|
|
|
|
function Clear-OfficeCache {
|
|
<#
|
|
.SYNOPSIS
|
|
Clean Microsoft Office caches and temp files
|
|
#>
|
|
|
|
$officeCachePaths = @(
|
|
# Office 365 / 2019 / 2021
|
|
"$env:LOCALAPPDATA\Microsoft\Office\16.0\OfficeFileCache"
|
|
"$env:LOCALAPPDATA\Microsoft\Office\16.0\Wef"
|
|
"$env:LOCALAPPDATA\Microsoft\Outlook\RoamCache"
|
|
"$env:LOCALAPPDATA\Microsoft\Outlook\Offline Address Books"
|
|
# Older Office versions
|
|
"$env:LOCALAPPDATA\Microsoft\Office\15.0\OfficeFileCache"
|
|
# Office temp files
|
|
"$env:APPDATA\Microsoft\Templates\*.tmp"
|
|
"$env:APPDATA\Microsoft\Word\*.tmp"
|
|
"$env:APPDATA\Microsoft\Excel\*.tmp"
|
|
"$env:APPDATA\Microsoft\PowerPoint\*.tmp"
|
|
)
|
|
|
|
foreach ($path in $officeCachePaths) {
|
|
if ($path -like "*.tmp") {
|
|
$parent = Split-Path -Parent $path
|
|
if (Test-Path $parent) {
|
|
$tmpFiles = Get-ChildItem -Path $parent -Filter "*.tmp" -File -ErrorAction SilentlyContinue
|
|
if ($tmpFiles) {
|
|
$paths = $tmpFiles | ForEach-Object { $_.FullName }
|
|
Remove-SafeItems -Paths $paths -Description "Office temp files"
|
|
}
|
|
}
|
|
}
|
|
elseif (Test-Path $path) {
|
|
Clear-DirectoryContents -Path $path -Description "Office $(Split-Path -Leaf $path)"
|
|
}
|
|
}
|
|
}
|
|
|
|
function Clear-OneDriveCache {
|
|
<#
|
|
.SYNOPSIS
|
|
Clean OneDrive cache
|
|
#>
|
|
|
|
$oneDriveCachePaths = @(
|
|
"$env:LOCALAPPDATA\Microsoft\OneDrive\logs"
|
|
"$env:LOCALAPPDATA\Microsoft\OneDrive\setup\logs"
|
|
)
|
|
|
|
foreach ($path in $oneDriveCachePaths) {
|
|
if (Test-Path $path) {
|
|
Remove-OldFiles -Path $path -DaysOld 7 -Description "OneDrive logs"
|
|
}
|
|
}
|
|
}
|
|
|
|
function Clear-DropboxCache {
|
|
<#
|
|
.SYNOPSIS
|
|
Clean Dropbox cache
|
|
#>
|
|
|
|
# Dropbox cache is typically in the Dropbox folder itself
|
|
$dropboxInfoPath = "$env:LOCALAPPDATA\Dropbox\info.json"
|
|
|
|
if (Test-Path $dropboxInfoPath) {
|
|
try {
|
|
$dropboxInfo = Get-Content $dropboxInfoPath | ConvertFrom-Json
|
|
$dropboxPath = $dropboxInfo.personal.path
|
|
|
|
if ($dropboxPath) {
|
|
$dropboxCachePath = "$dropboxPath\.dropbox.cache"
|
|
if (Test-Path $dropboxCachePath) {
|
|
Clear-DirectoryContents -Path $dropboxCachePath -Description "Dropbox cache"
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
Write-Debug "Could not read Dropbox config: $_"
|
|
}
|
|
}
|
|
}
|
|
|
|
function Clear-GoogleDriveCache {
|
|
<#
|
|
.SYNOPSIS
|
|
Clean Google Drive cache
|
|
#>
|
|
|
|
$googleDriveCachePaths = @(
|
|
"$env:LOCALAPPDATA\Google\DriveFS\Logs"
|
|
"$env:LOCALAPPDATA\Google\DriveFS\*.tmp"
|
|
)
|
|
|
|
foreach ($path in $googleDriveCachePaths) {
|
|
if ($path -like "*.tmp") {
|
|
$parent = Split-Path -Parent $path
|
|
if (Test-Path $parent) {
|
|
$tmpFiles = Get-ChildItem -Path $parent -Filter "*.tmp" -ErrorAction SilentlyContinue
|
|
if ($tmpFiles) {
|
|
$paths = $tmpFiles | ForEach-Object { $_.FullName }
|
|
Remove-SafeItems -Paths $paths -Description "Google Drive temp"
|
|
}
|
|
}
|
|
}
|
|
elseif (Test-Path $path) {
|
|
Remove-OldFiles -Path $path -DaysOld 7 -Description "Google Drive logs"
|
|
}
|
|
}
|
|
}
|
|
|
|
function Clear-AdobeData {
|
|
<#
|
|
.SYNOPSIS
|
|
Clean Adobe application caches and temp files
|
|
#>
|
|
|
|
$adobeCachePaths = @(
|
|
"$env:APPDATA\Adobe\Common\Media Cache Files"
|
|
"$env:APPDATA\Adobe\Common\Peak Files"
|
|
"$env:APPDATA\Adobe\Common\Team Projects Cache"
|
|
"$env:LOCALAPPDATA\Adobe\*\Cache"
|
|
"$env:LOCALAPPDATA\Adobe\*\CameraRaw\Cache"
|
|
"$env:LOCALAPPDATA\Temp\Adobe"
|
|
)
|
|
|
|
foreach ($pattern in $adobeCachePaths) {
|
|
$paths = Resolve-Path $pattern -ErrorAction SilentlyContinue
|
|
foreach ($path in $paths) {
|
|
if (Test-Path $path.Path) {
|
|
Clear-DirectoryContents -Path $path.Path -Description "Adobe cache"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function Clear-AutodeskData {
|
|
<#
|
|
.SYNOPSIS
|
|
Clean Autodesk application caches
|
|
#>
|
|
|
|
$autodeskCachePaths = @(
|
|
"$env:LOCALAPPDATA\Autodesk\*\Cache"
|
|
"$env:APPDATA\Autodesk\*\cache"
|
|
)
|
|
|
|
foreach ($pattern in $autodeskCachePaths) {
|
|
$paths = Resolve-Path $pattern -ErrorAction SilentlyContinue
|
|
foreach ($path in $paths) {
|
|
if (Test-Path $path.Path) {
|
|
Clear-DirectoryContents -Path $path.Path -Description "Autodesk cache"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# Gaming Platform Cleanup
|
|
# ============================================================================
|
|
|
|
function Clear-GamingPlatformCaches {
|
|
<#
|
|
.SYNOPSIS
|
|
Clean gaming platform caches (Steam, Epic, Origin, etc.)
|
|
#>
|
|
|
|
# Steam
|
|
$steamPaths = @(
|
|
"${env:ProgramFiles(x86)}\Steam\appcache\httpcache"
|
|
"${env:ProgramFiles(x86)}\Steam\appcache\librarycache"
|
|
"${env:ProgramFiles(x86)}\Steam\logs"
|
|
)
|
|
foreach ($path in $steamPaths) {
|
|
if (Test-Path $path) {
|
|
Clear-DirectoryContents -Path $path -Description "Steam $(Split-Path -Leaf $path)"
|
|
}
|
|
}
|
|
|
|
# Epic Games Launcher
|
|
$epicPaths = @(
|
|
"$env:LOCALAPPDATA\EpicGamesLauncher\Saved\webcache"
|
|
"$env:LOCALAPPDATA\EpicGamesLauncher\Saved\Logs"
|
|
)
|
|
foreach ($path in $epicPaths) {
|
|
if (Test-Path $path) {
|
|
Clear-DirectoryContents -Path $path -Description "Epic Games $(Split-Path -Leaf $path)"
|
|
}
|
|
}
|
|
|
|
# EA App (Origin replacement)
|
|
$eaPaths = @(
|
|
"$env:LOCALAPPDATA\Electronic Arts\EA Desktop\cache"
|
|
"$env:APPDATA\Origin\*\cache"
|
|
)
|
|
foreach ($pattern in $eaPaths) {
|
|
$paths = Resolve-Path $pattern -ErrorAction SilentlyContinue
|
|
foreach ($path in $paths) {
|
|
if (Test-Path $path.Path) {
|
|
Clear-DirectoryContents -Path $path.Path -Description "EA/Origin cache"
|
|
}
|
|
}
|
|
}
|
|
|
|
# GOG Galaxy
|
|
$gogPaths = @(
|
|
"$env:LOCALAPPDATA\GOG.com\Galaxy\webcache"
|
|
"$env:PROGRAMDATA\GOG.com\Galaxy\logs"
|
|
)
|
|
foreach ($path in $gogPaths) {
|
|
if (Test-Path $path) {
|
|
Clear-DirectoryContents -Path $path -Description "GOG Galaxy $(Split-Path -Leaf $path)"
|
|
}
|
|
}
|
|
|
|
# Ubisoft Connect
|
|
$ubiPaths = @(
|
|
"$env:LOCALAPPDATA\Ubisoft Game Launcher\cache"
|
|
"$env:LOCALAPPDATA\Ubisoft Game Launcher\logs"
|
|
)
|
|
foreach ($path in $ubiPaths) {
|
|
if (Test-Path $path) {
|
|
Clear-DirectoryContents -Path $path -Description "Ubisoft $(Split-Path -Leaf $path)"
|
|
}
|
|
}
|
|
|
|
# Battle.net
|
|
$battlenetPaths = @(
|
|
"$env:APPDATA\Battle.net\Cache"
|
|
"$env:APPDATA\Battle.net\Logs"
|
|
)
|
|
foreach ($path in $battlenetPaths) {
|
|
if (Test-Path $path) {
|
|
Clear-DirectoryContents -Path $path -Description "Battle.net $(Split-Path -Leaf $path)"
|
|
}
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# Main Application Cleanup Function
|
|
# ============================================================================
|
|
|
|
function Invoke-AppCleanup {
|
|
<#
|
|
.SYNOPSIS
|
|
Run all application-specific cleanup tasks
|
|
#>
|
|
param([switch]$IncludeOrphaned)
|
|
|
|
Start-Section "Applications"
|
|
|
|
# Productivity apps
|
|
Clear-OfficeCache
|
|
Clear-OneDriveCache
|
|
Clear-DropboxCache
|
|
Clear-GoogleDriveCache
|
|
|
|
# Creative apps
|
|
Clear-AdobeData
|
|
Clear-AutodeskData
|
|
|
|
# Gaming platforms
|
|
Clear-GamingPlatformCaches
|
|
|
|
Stop-Section
|
|
|
|
# Orphaned app data (separate section)
|
|
if ($IncludeOrphaned) {
|
|
Clear-OrphanedAppData -DaysOld 60
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# Exports
|
|
# ============================================================================
|
|
# Functions: Get-InstalledPrograms, Find-OrphanedAppData, Clear-OfficeCache, etc.
|