# 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 # move -> move arquivos para uma subpasta da pasta escolhida no site (dest=) # 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 # Só obedece ao site AOS 3D Tools: abrir e Lixeira exigem a chave secreta criada aqui no # primeiro teste e entregue apenas à página oficial do site. Pedido sem a chave é recusado. 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('\') $ChaveFile = Join-Path $Base 'chave.txt' function Chave { if (-not (Test-Path -LiteralPath $ChaveFile) -or -not (Get-Content -LiteralPath $ChaveFile -Raw)) { $b = New-Object byte[] 16; [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b) Set-Content -LiteralPath $ChaveFile -Value (($b | ForEach-Object { $_.ToString('x2') }) -join '') -NoNewline -Encoding ASCII } return (Get-Content -LiteralPath $ChaveFile -Raw).Trim() } 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) # tudo além do teste precisa da chave: links de outros sites não sabem a chave if ($acao -ne 'ping') { $k = $q['k'] if (-not $k -or $k -ne (Chave)) { Log "recusado: pedido sem a chave do site ($acao)"; Aviso 'Recusei um pedido que não veio do site AOS 3D Tools.'; exit 1 } } 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) # a chave vai só para a página oficial, depois do # (não sai do navegador) Start-Process ('https://aos3dtools.pages.dev/conector/ok/#k=' + (Chave)); 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)) } } } 'move' { $dest = $q['dest'] if ($dest -and ($dest -match '\.\.' -or $dest -match '^[\\/]' -or $dest -match ':')) { Log 'recusado: destino inválido'; exit 1 } $raiz = AchaRaiz ($rels | Select-Object -First 1) if (-not $raiz) { Aviso 'Não achei a pasta no computador.'; exit 1 } Lembra $raiz $pasta = $raiz; if ($dest) { $pasta = Join-Path $raiz ($dest -replace '/', '\') } if (-not (DentroDeCasa (Join-Path $pasta 'x'))) { Log 'recusado: fora da pasta do usuário'; exit 1 } New-Item -ItemType Directory -Force -Path $pasta | Out-Null $feitos = 0; $pulos = 0 foreach ($r in $rels) { $a = Caminho $r if (-not $a -or -not (So3D $a) -or -not (DentroDeCasa $a) -or -not (Test-Path -LiteralPath $a)) { continue } $alvo = Join-Path $pasta (Split-Path $a -Leaf) if (Test-Path -LiteralPath $alvo) { $pulos++; continue } # nunca sobrescreve try { Move-Item -LiteralPath $a -Destination $alvo; $feitos++; Log "movido: $a -> $pasta" } catch { } } $msg = "$feitos arquivo(s) movido(s)"; if ($pulos) { $msg += ", $pulos já existia(m) no destino" } Aviso ($msg + '.') } default { Log "ação desconhecida: $acao" } }