modules/CheatLab.Commands.ps1
|
function Show-CheatLabHelp { Write-Host "`n=== CheatLab PowerShell Client === " -ForegroundColor Cyan Write-Host "Usage: cheat [method] [endpoint] [options]`n" -ForegroundColor White Write-Host "METHODS:" -ForegroundColor Yellow Write-Host " get - Retrieve content by ID/key/endpoint" Write-Host " post - Create new text entry (stored in database)" Write-Host " delete - Delete cheat entry" Write-Host " ai - Interactive AI mode or single prompt" Write-Host " config - Configure credentials" Write-Host " upload - Upload file(s) - supports single/multi-file, any type" Write-Host " download - Download file(s) with automatic filename preservation" Write-Host "" Write-Host "COMMON OPTIONS:" -ForegroundColor Yellow Write-Host " -text, -t Text content" Write-Host " -key, -k Custom key identifier" Write-Host " -who, -w Author name" Write-Host " -password, -p Password protection" Write-Host " -admin_pass, -ap Admin password" Write-Host " -protect_view, -pvi Protect from viewing" Write-Host " -protect_delete, -pd Protect from deletion" Write-Host " -confidential, -c Auto-enable both protections" Write-Host " -days <1-30> Set upload expiry days (default 3, max 30)" Write-Host " -details, -d Show detailed info" Write-Host "" Write-Host "UPLOAD OPTIONS:" -ForegroundColor Yellow Write-Host " -clipboard, -cb Upload files from clipboard (Windows Explorer only)" Write-Host " * Shows file list with sizes for confirmation" Write-Host " * Rejects text clipboard (use 'cheat post' instead)" Write-Host " * Supports multi-file (auto-creates ZIP)" Write-Host "" Write-Host "DOWNLOAD OPTIONS:" -ForegroundColor Yellow Write-Host " -o, -outfile <name> Specify custom output filename" Write-Host " * Default: uses original filename from upload" Write-Host " * Multi-file: prompts to extract ZIP" Write-Host "" Write-Host "HELPER FUNCTIONS:" -ForegroundColor Yellow Write-Host " file <path> Read file contents as a raw string" Write-Host " (available when cheat.ps1 is dot-sourced)" Write-Host " update-cheat Download and install the latest CheatLab update" Write-Host "" Write-Host "EXAMPLES:" -ForegroundColor Yellow Write-Host " # Configuration" -ForegroundColor Gray Write-Host " cheat config -username 'user' -auth_key 'key'" Write-Host "" Write-Host " # Text operations (stored in database)" -ForegroundColor Gray Write-Host " cheat post -t 'Hello World' -k 'greeting'" Write-Host " cheat get latest" Write-Host " cheat get greeting -p 'secret'" Write-Host "" Write-Host " # File uploads (any type, stored in Supabase)" -ForegroundColor Gray Write-Host " cheat upload file.zip -k 'archive' -c" Write-Host " cheat upload doc.pdf image.png data.json -k 'multi' (auto-zip)" Write-Host " cheat upload -cb -k 'doc' (clipboard, shows confirmation)" Write-Host "" Write-Host " # File downloads" -ForegroundColor Gray Write-Host " cheat download 5 (uses original filename)" Write-Host " cheat download 5 -o myfile.txt (custom name)" Write-Host "" Write-Host " # Other features" -ForegroundColor Gray Write-Host " cheat ai 'how to use PowerShell'" Write-Host " cheat delete 123 -p 'password'" Write-Host " file USER_MANUAL.txt (helper function)" Write-Host " update-cheat (update CheatLab to latest)" Write-Host "" Write-Host "NOTES:" -ForegroundColor Yellow Write-Host " * Multi-file uploads automatically create ZIP archives" Write-Host " * Clipboard upload only accepts files (copy in Explorer)" Write-Host " * For text content, use 'cheat post' to save database storage" Write-Host " * Original filenames are preserved in downloads" Write-Host " * All file types are supported (no MIME restrictions)" Write-Host "" } function Invoke-CheatConfig { param( [string]$Username, [string]$AuthKey ) Set-CheatCredentials -Username $Username -AuthKey $AuthKey } function Invoke-CheatAi { param( [string]$BaseUrl, [string]$Endpoint ) function Send-AiPrompt { param([string]$prompt) $body = @{ prompt = $prompt history = $global:CheatAiHistory } $url = "$BaseUrl/ai?username=$global:CheatUsername&auth_key=$global:CheatAuthKey" $reply = Invoke-CheatApi -url $url -method POST -body $body -timeoutSec 60 if ($reply) { $replyText = $reply -join "`n" $width = $Host.UI.RawUI.WindowSize.Width - 1 $wrapped = foreach ($line in ($replyText -split "`n")) { if ($line.Length -le $width) { $line } else { $words = $line -split ' ' $current = "" foreach ($word in $words) { if ($current.Length -eq 0) { $current = $word } elseif (($current.Length + 1 + $word.Length) -le $width) { $current += " $word" } else { $current $current = $word } } if ($current.Length -gt 0) { $current } } } Write-Host "" Write-Host ($wrapped -join "`n") -ForegroundColor Magenta Write-Host "" $global:CheatAiHistory += @{ role = "user"; content = $prompt } $global:CheatAiHistory += @{ role = "assistant"; content = $replyText } } } if ($Endpoint -eq "clear") { $global:CheatAiHistory = @() Write-Host "Chat history cleared." -ForegroundColor Green return } if ($Endpoint) { Send-AiPrompt $Endpoint return } while ($true) { $p = Read-Host "Prompt" if ($p -eq "exit") { break } if ($p -eq "clear") { $global:CheatAiHistory = @() Write-Host "Chat history cleared." -ForegroundColor Green continue } Send-AiPrompt $p } } function Invoke-CheatGet { param( [string]$BaseUrl, [string]$Endpoint, [string]$Password, [string]$AdminPass, [switch]$Details ) $path = Resolve-Endpoint $Endpoint $url = Build-Url "$BaseUrl$path" @{ password = $Password admin_pass = $AdminPass details = if ($Details) { "true" } } Invoke-CheatApi -url $url } function Invoke-CheatPost { param( [string]$BaseUrl, [string]$Endpoint, [string]$Text, [string]$Who, [string]$Password, [string]$Key, [switch]$ProtectView, [switch]$ProtectDelete, [switch]$Confidential ) if (-not $Text -and $Endpoint) { $Text = $Endpoint } $body = @{ text = $Text } if ($Who) { $body.who = $Who } if ($Password) { $body.password = $Password } if ($Key) { $body.key = $Key } if ($ProtectView) { $body.protect_view = $true } if ($ProtectDelete) { $body.protect_delete = $true } if ($Confidential) { $body.confidential = $true } Invoke-CheatApi -url "$BaseUrl/new?username=$global:CheatUsername&auth_key=$global:CheatAuthKey" -method POST -body $body } function Invoke-CheatDelete { param( [string]$BaseUrl, [string]$Endpoint, [string]$Password, [string]$AdminPass ) if (-not $Endpoint) { Write-Host "Error: endpoint required" -ForegroundColor Red return } $queryParams = @{} if ($Password) { $queryParams.password = $Password } if ($AdminPass) { $queryParams.admin_pass = $AdminPass } if ($Endpoint -eq "latest") { $url = Build-Url "$BaseUrl/delete/latest" $queryParams } else { $path = Resolve-Endpoint $Endpoint $url = Build-Url "$BaseUrl$path" $queryParams } try { $response = Invoke-RestMethod -Uri $url -Method DELETE -TimeoutSec 30 Write-Host $response -ForegroundColor White } catch { if ($_.Exception.Message -match "timeout|timed out") { Write-Error "Delete request timed out (database may be slow)" } else { Write-Host ('Error: ' + $_.Exception.Message) -ForegroundColor Red } } } function Invoke-CheatUpload { param( [string]$BaseUrl, [string]$Endpoint, [string[]]$AdditionalFiles, [string]$Key, [string]$Password, [string]$Who, [int]$Days, [switch]$ProtectView, [switch]$ProtectDelete, [switch]$Confidential, [switch]$Clipboard ) $filePaths = @() if ($Clipboard) { $clipboardFiles = $null try { $clipboardFiles = Get-Clipboard -Format FileDropList -ErrorAction SilentlyContinue } catch { } if ($clipboardFiles -and $clipboardFiles.Count -gt 0) { Write-Host "`nDetected $($clipboardFiles.Count) file(s) in clipboard:" -ForegroundColor Cyan foreach ($file in $clipboardFiles) { $fileInfo = Get-Item $file -ErrorAction SilentlyContinue if ($fileInfo) { $sizeKB = [math]::Round($fileInfo.Length / 1KB, 2) $sizeMB = [math]::Round($fileInfo.Length / 1MB, 2) $sizeStr = if ($sizeMB -ge 1) { "$sizeMB MB" } else { "$sizeKB KB" } Write-Host " - $($fileInfo.Name) ($sizeStr)" -ForegroundColor Gray } } Write-Host "" $confirmation = Read-Host "Upload these file(s)? (Y/N)" if ($confirmation -notmatch '^[Yy]') { Write-Host "Upload cancelled" -ForegroundColor Yellow return } $filePaths = @($clipboardFiles) } else { Write-Host "Error: Clipboard must contain files (copied from Explorer)" -ForegroundColor Red Write-Host "For text content, use: cheat post -t 'your text' -k 'key'" -ForegroundColor Yellow return } } else { if ($Endpoint) { $filePaths += $Endpoint } if ($AdditionalFiles -and $AdditionalFiles.Count -gt 0) { $filePaths += $AdditionalFiles } if ($filePaths.Count -eq 0) { Write-Host "Error: No files specified" -ForegroundColor Red return } } if (-not $Clipboard) { foreach ($fp in $filePaths) { if (-not (Test-Path $fp)) { Write-Host "File not found: $fp" -ForegroundColor Red return } } } $isMultiFile = $filePaths.Count -gt 1 $tempZipCreated = $false $multiFileCount = 0 if ($isMultiFile) { $multiFileCount = $filePaths.Count Write-Host "Multiple files detected ($multiFileCount files) - creating zip archive..." -ForegroundColor Cyan $tempZipName = "cheatlab_multifile_$(Get-Date -Format 'yyyyMMddHHmmss').zip" $tempZipPath = Join-Path $PWD $tempZipName try { Compress-Archive -Path $filePaths -DestinationPath $tempZipPath -Force Write-Host "Archive created: $tempZipName" -ForegroundColor Green $file = Get-Item $tempZipPath $tempZipCreated = $true } catch { Write-Host "Failed to create zip: $($_.Exception.Message)" -ForegroundColor Red return } } else { $file = Get-Item $filePaths[0] } $mimeMap = @{ ".png" = "image/png" ".jpg" = "image/jpeg" ".jpeg" = "image/jpeg" ".pdf" = "application/pdf" ".pptx" = "application/vnd.openxmlformats-officedocument.presentationml.presentation" ".zip" = "application/zip" ".txt" = "text/plain" ".js" = "text/javascript" ".ts" = "text/typescript" ".py" = "text/x-python" ".cs" = "text/x-csharp" ".json" = "application/json" ".xml" = "application/xml" ".html" = "text/html" ".css" = "text/css" ".md" = "text/markdown" ".ps1" = "text/plain" ".sh" = "text/x-shellscript" } $ext = $file.Extension.ToLower() if ($mimeMap.ContainsKey($ext)) { $mime = $mimeMap[$ext] } else { if ($Clipboard) { $mime = "text/plain" } else { Write-Host "Unknown extension; sending as application/octet-stream" -ForegroundColor Yellow $mime = "application/octet-stream" } } try { Write-Host "Initializing upload..." -ForegroundColor Gray $init = Invoke-RestMethod ` -Uri "$BaseUrl/upload?username=$global:CheatUsername&auth_key=$global:CheatAuthKey" ` -Method POST ` -Body (@{ filename = $file.Name; mime = $mime } | ConvertTo-Json) ` -ContentType "application/json" ` -TimeoutSec 30 } catch { if ($_.Exception.Message -match "timeout|timed out") { Write-Host "Upload initialization timed out (database may be slow)" -ForegroundColor Red } else { Write-Host "Upload init failed: $($_.Exception.Message)" -ForegroundColor Red } return } Write-Host "Uploading file..." -ForegroundColor Gray Invoke-WebRequest -UseBasicParsing -Uri $init.upload_url -Method PUT -InFile $file.FullName -TimeoutSec 90 Write-Host "Processing upload..." -ForegroundColor Gray $expiryDays = if ($Days) { $Days } else { 3 } $expirySeconds = $expiryDays * 24 * 60 * 60 $dlBody = @{ path = $init.path; expires_days = $expiryDays; ttl = $expirySeconds } $dl = Invoke-RestMethod ` -Uri "$BaseUrl/download?username=$global:CheatUsername&auth_key=$global:CheatAuthKey" ` -Method POST ` -Body ($dlBody | ConvertTo-Json) ` -ContentType "application/json" ` -TimeoutSec 30 $body = @{ text = $dl.download_url } if ($Key) { $body.key = $Key } if ($Password) { $body.password = $Password } if ($Who) { $body.who = $Who } if ($isMultiFile) { $body.metadata = @{ type = "multi-file"; file_count = $multiFileCount } } if ($ProtectView) { $body.protect_view = $true } if ($ProtectDelete) { $body.protect_delete = $true } if ($Confidential) { $body.confidential = $true } $postUrl = "$BaseUrl/new?username=$global:CheatUsername&auth_key=$global:CheatAuthKey" Write-Host "Finalizing upload (POST /new)..." -ForegroundColor Gray try { Invoke-RestMethod -Uri $postUrl -Method POST -Body ($body | ConvertTo-Json) -ContentType "application/json" -TimeoutSec 30 } catch { Write-Host "Finalization failed: $($_.Exception.Message)" -ForegroundColor Red if ($_.Exception.Response) { try { $stream = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($stream) $responseBody = $reader.ReadToEnd() $reader.Close() $stream.Close() if ($responseBody) { Write-Host "Server response: $responseBody" -ForegroundColor Yellow } } catch { } } } if ($tempZipCreated -and (Test-Path $file.FullName)) { Remove-Item $file.FullName -Force Write-Host "Temporary archive cleaned up" -ForegroundColor Gray } if ($Clipboard -and (Test-Path $file.FullName)) { Remove-Item $file.FullName -Force -ErrorAction SilentlyContinue } if ($Clipboard) { Write-Host "Clipboard uploaded as: $($file.Name)" -ForegroundColor Green } else { Write-Host "Upload complete" -ForegroundColor Green } } function Invoke-CheatDownload { param( [string]$BaseUrl, [string]$Endpoint, [string]$Password, [string]$AdminPass, [string]$Outfile, [string[]]$AdditionalFiles ) $path = Resolve-Endpoint $Endpoint $detailsUrl = Build-Url "$BaseUrl$path" @{ password = $Password admin_pass = $AdminPass details = "true" } Write-Host "Getting file info..." -ForegroundColor Gray try { $fileInfo = Invoke-RestMethod -Uri $detailsUrl -TimeoutSec 30 } catch { Write-Host "Failed to get file info" -ForegroundColor Red Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Yellow if ($_.ErrorDetails.Message) { Write-Host "Details: $($_.ErrorDetails.Message)" -ForegroundColor Yellow } return } $isMultiFile = $false $fileCount = 0 if ($fileInfo.metadata) { try { $meta = if ($fileInfo.metadata -is [string]) { $fileInfo.metadata | ConvertFrom-Json } else { $fileInfo.metadata } if ($meta.type -eq "multi-file") { $isMultiFile = $true $fileCount = [int]$meta.file_count } } catch { $isMultiFile = $false } } $url = Build-Url "$BaseUrl$path" @{ password = $Password admin_pass = $AdminPass } try { $dl = Invoke-RestMethod -Uri $url -TimeoutSec 30 } catch { Write-Host "Failed to get download URL: $($_.Exception.Message)" -ForegroundColor Red return } if ($dl -notmatch '^https?://') { Write-Host "Error: Response is not a valid download URL" -ForegroundColor Red Write-Host "Received: $dl" -ForegroundColor Yellow return } $extractedFilename = $null if (-not $isMultiFile) { try { $urlPath = ([uri]$dl).AbsolutePath $filenameWithTimestamp = Split-Path $urlPath -Leaf if ($filenameWithTimestamp -match '^\d{13}_(.+)$') { $extractedFilename = $matches[1] } } catch { $extractedFilename = $null } } $out = if ($Outfile) { $Outfile } elseif ($AdditionalFiles -and $AdditionalFiles.Count -gt 0) { $AdditionalFiles[0] } elseif ($extractedFilename) { $extractedFilename } else { "cheatlab_$Endpoint.zip" } $shouldExtract = $false if ($isMultiFile) { Write-Host "" Write-Host "This is a zip archive containing $fileCount files." -ForegroundColor Cyan $response = Read-Host "Extract files to current folder? (Y/n)" $shouldExtract = $response -eq "" -or $response -eq "Y" -or $response -eq "y" if ($shouldExtract) { $out = "cheatlab_temp_$Endpoint.zip" } } Write-Host "Downloading file..." -ForegroundColor Gray try { Invoke-WebRequest -UseBasicParsing -Uri $dl -OutFile $out -TimeoutSec 90 if ($shouldExtract) { Write-Host "Extracting files..." -ForegroundColor Gray try { Expand-Archive -Path $out -DestinationPath "." -Force Remove-Item $out -Force Write-Host "Extracted $fileCount files to current folder" -ForegroundColor Green } catch { Write-Host "Extraction failed: $($_.Exception.Message)" -ForegroundColor Red Write-Host "Zip file saved as: $out" -ForegroundColor Yellow } } else { Write-Host "Saved as $out" -ForegroundColor Green } } catch { Write-Host "Download failed: $($_.Exception.Message)" -ForegroundColor Red } } function htype { param( [string]$Text, [string]$FilePath ) Add-Type -AssemblyName System.Windows.Forms if ($FilePath) { if (Test-Path $FilePath) { $Text = Get-Content -Path $FilePath -Raw Write-Host "Loaded file: $FilePath ($($Text.Length) characters)" } else { Write-Error "File not found: $FilePath" return } } if (-not $Text) { Write-Error "Please provide either -Text or -FilePath parameter" return } Write-Host "Focus your editor now. Typing starts in 5 seconds..." Start-Sleep -Seconds 5 $tokens = @() $allChars = $Text.ToCharArray() $i = 0 while ($i -lt $allChars.Length) { $tokenSize = Get-Random -Minimum 5 -Maximum 10 $tokenSize = [Math]::Min($tokenSize, $allChars.Length - $i) $token = -join $allChars[$i..($i + $tokenSize - 1)] $tokens += $token $i += $tokenSize } foreach ($token in $tokens) { [System.Windows.Forms.Clipboard]::SetText($token) [System.Windows.Forms.SendKeys]::SendWait('^v') [System.Windows.Forms.SendKeys]::SendWait('{LEFT}') [System.Windows.Forms.SendKeys]::SendWait('{RIGHT}') } } function update-cheat { param() Write-Host "Downloading CheatLab installer..." -ForegroundColor Cyan try { $url = "https://cheatlab.onrender.com/install-cheat" $script = Invoke-RestMethod -Uri $url if ($null -eq $script -or $script -eq "") { Write-Error "Failed to download installer or empty response received." return } Write-Host "Running installer script..." -ForegroundColor Cyan Invoke-Expression $script } catch { Write-Error "update-cheat failed: $($_.Exception.Message)" } } |