# AOS 3D Tools · Conector do Fatiador (Windows) # Recebe links aos3d://?root=&rel=&... # Ações: # open -> abre o arquivo no fatiador escolhido (app=anycubic|bambu|orca|creality|padrao) # trash -> manda para a Lixeira, sempre depois de confirmar numa janela do próprio Windows # ping -> teste: abre a página de confirmação do site (endereço fixo) # Regras de segurança: só arquivos 3D (.3mf .stl .obj .step .stp), só dentro da pasta do # usuário, sem acesso à internet, nada é enviado para lugar nenhum. # Registro local em %LOCALAPPDATA%\AOS3DTools\ajudante.log param([string]$Url) Add-Type -AssemblyName System.Web, System.Windows.Forms, Microsoft.VisualBasic $Base = Join-Path $env:LOCALAPPDATA 'AOS3DTools' $LogFile = Join-Path $Base 'ajudante.log' $PastasFile = Join-Path $Base 'pastas.txt' $Casa = [System.IO.Path]::GetFullPath($env:USERPROFILE).TrimEnd('\') function Log([string]$m) { try { Add-Content -LiteralPath $LogFile -Value ("{0} {1}" -f (Get-Date -Format s), $m) -Encoding UTF8 } catch {} } function Aviso([string]$m) { [void][System.Windows.Forms.MessageBox]::Show($m, 'Conector do Fatiador') } function So3D([string]$c) { return $c -match '\.(3mf|stl|obj|step|stp)$' } function DentroDeCasa([string]$c) { try { $f = [System.IO.Path]::GetFullPath($c); return $f.StartsWith($Casa + '\', [StringComparison]::OrdinalIgnoreCase) } catch { return $false } } try { $u = [uri]$Url $acao = $u.Host $q = [System.Web.HttpUtility]::ParseQueryString($u.Query) } catch { Log "link inválido"; exit 1 } $root = $q['root']; $name = $q['name']; $dl = $q['dl']; $app = $q['app'] $rels = @($q.GetValues('rel')) | Where-Object { $_ } Log ("acao={0} root={1} arquivos={2} name={3} dl={4} app={5}" -f $acao, $root, $rels.Count, $name, $dl, $app) if (($name -match '[\\/]') -or ($root -match '[\\/]|\.\.')) { Log 'recusado: nome inválido'; exit 1 } # Descobre o caminho completo da pasta escolhida no site (o navegador não informa) function AchaRaiz([string]$rel) { if (Test-Path -LiteralPath $PastasFile) { foreach ($l in Get-Content -LiteralPath $PastasFile -Encoding UTF8) { $k, $v = $l -split "`t", 2 if ($k -eq $root -and $v -and (Test-Path -LiteralPath (Join-Path $v $rel))) { return $v } } } $cands = @("$Casa\$root", "$Casa\Downloads\$root", "$Casa\Desktop\$root", "$Casa\Documents\$root", "$Casa\OneDrive\$root", "$Casa\OneDrive\Desktop\$root", "$Casa\OneDrive\Documents\$root", "$Casa\Downloads") foreach ($c in $cands) { if ((Split-Path $c -Leaf) -eq $root -and (Test-Path -LiteralPath (Join-Path $c $rel))) { return $c } } $achou = Get-ChildItem -LiteralPath $Casa -Directory -Recurse -Depth 4 -Filter $root -ErrorAction SilentlyContinue | Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName $rel) } | Select-Object -First 1 if ($achou) { return $achou.FullName } return $null } function Lembra([string]$raiz) { $linhas = @() if (Test-Path -LiteralPath $PastasFile) { $linhas = @(Get-Content -LiteralPath $PastasFile -Encoding UTF8 | Where-Object { -not $_.StartsWith("$root`t") }) } $linhas += "$root`t$raiz" Set-Content -LiteralPath $PastasFile -Value $linhas -Encoding UTF8 } function Caminho([string]$rel) { if ($rel -match '\.\.') { return $null } if ($dl) { return (Join-Path (Join-Path $Casa 'Downloads') $name) } $raiz = AchaRaiz $rel if (-not $raiz) { return $null } Lembra $raiz $c = Join-Path $raiz $rel if ($name) { $c = Join-Path (Split-Path $c -Parent) $name } return $c } function Espera([string]$c) { for ($i = 0; $i -lt 60; $i++) { if (Test-Path -LiteralPath $c) { return $true }; Start-Sleep -Milliseconds 500 }; return $false } # Procura o fatiador instalado pela lista de programas do Windows function AchaFatiador([string]$qual) { $nomes = @{ anycubic = 'Anycubic*Slicer*'; bambu = 'Bambu Studio*'; orca = 'OrcaSlicer*'; creality = 'Creality Print*' } if (-not $qual -or -not $nomes.ContainsKey($qual)) { return $null } $chaves = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' $e = Get-ItemProperty $chaves -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like $nomes[$qual] } | Select-Object -First 1 if (-not $e) { return $null } $exe = $null if ($e.DisplayIcon) { $exe = ($e.DisplayIcon -split ',')[0].Trim('"') } if (-not $exe -or -not $exe.ToLower().EndsWith('.exe') -or -not (Test-Path -LiteralPath $exe)) { if ($e.InstallLocation -and (Test-Path -LiteralPath $e.InstallLocation)) { $exe = Get-ChildItem -LiteralPath $e.InstallLocation -Filter *.exe -ErrorAction SilentlyContinue | Where-Object { $_.Name -notmatch 'unins|uninstall|update|crash|helper' } | Sort-Object Length -Descending | Select-Object -First 1 -ExpandProperty FullName } } return $exe } switch ($acao) { 'ping' { # teste pedido pelo site: responde abrindo a página de confirmação (endereço fixo) Start-Process 'https://aos3dtools.pages.dev/conector/ok/'; Log 'teste ok' } 'open' { $alvo = Caminho ($rels | Select-Object -First 1) if (-not $alvo) { Aviso 'Não achei o arquivo no computador.'; exit 1 } if (-not (So3D $alvo) -or -not (DentroDeCasa $alvo)) { Log "recusado: $alvo"; exit 1 } if (-not (Espera $alvo)) { Aviso ("O arquivo {0} não apareceu a tempo." -f (Split-Path $alvo -Leaf)); exit 1 } $exe = AchaFatiador $app if ($exe) { Start-Process -FilePath $exe -ArgumentList ('"{0}"' -f $alvo); Log "aberto com $exe" } else { Start-Process -FilePath $alvo Log 'aberto com o programa padrão' if ($app -and $app -ne 'padrao') { Aviso 'Não achei o fatiador escolhido. Abri com o programa padrão do Windows.' } } } 'trash' { $alvos = @() foreach ($r in $rels) { $a = Caminho $r if ($a -and (So3D $a) -and (DentroDeCasa $a) -and (Test-Path -LiteralPath $a)) { $alvos += $a } } if ($alvos.Count -eq 0) { Aviso 'Nenhum arquivo para mandar para a Lixeira.'; exit 1 } $lista = ($alvos | Select-Object -First 8 | ForEach-Object { Split-Path $_ -Leaf }) -join "`n" if ($alvos.Count -gt 8) { $lista += "`ne mais $($alvos.Count - 8)" } $r = [System.Windows.Forms.MessageBox]::Show("Mandar $($alvos.Count) arquivo(s) para a Lixeira?`n`n$lista", 'Conector do Fatiador', 'YesNo', 'Question') if ($r -ne 'Yes') { Log 'lixeira cancelada'; exit 0 } foreach ($a in $alvos) { try { [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($a, 'OnlyErrorDialogs', 'SendToRecycleBin'); Log "lixeira: $a" } catch { Aviso ("Não consegui mandar {0} para a Lixeira." -f (Split-Path $a -Leaf)) } } } default { Log "ação desconhecida: $acao" } }