Private/Get-GistContent.ps1

function Get-GistContent {
  <#
    .SYNOPSIS
        A short one-line action-based description, e.g. 'Tests if a function is valid'
    .DESCRIPTION
        A longer description of the function, its purpose, common use cases, etc.
    .NOTES
        Information or caveats about the function e.g. 'This function is not supported in Linux'
    .LINK
        Specify a URI to a help page, this will show when Get-Help -Online is used.
    .EXAMPLE
        Get-GistContent https://gist.github.com/alainQtec/34c60b903f3c1c9d51ec5f0ee3a82adb
        Will get contents of the first file found in the gist
    #>

  [OutputType([string])]
  [CmdletBinding()]
  param (
    # File name with extention
    [Parameter(Mandatory = $false, Position = 0)]
    [string]$FileName,
    # url string, can be a raw_url or just a normal gist url
    [Parameter(Mandatory = $false)]
    [string]$GistUrl
  )

  begin {
    enum EncryptionScope {
      User    # The encrypted data can be decrypted with the same user on any machine.
      Machine # The encrypted data can only be decrypted with the same user on the same machine it was encrypted on.
    }
    enum Compression {
      Gzip
      Deflate
      ZLib
    }

    class GitHub {
      static $webSession
      static [string] $UserName
      static hidden [bool] $IsInteractive = $false
      static hidden [string] $TokenFile = [GitHub]::GetTokenFile()

      static [PSObject] createSession() {
        return [Github]::createSession([Github]::UserName)
      }
      static [PSObject] createSession([string]$UserName) {
        [GitHub]::SetToken()
        return [GitHub]::createSession($UserName, [GitHub]::GetToken())
      }
      static [Psobject] createSession([string]$GitHubUserName, [securestring]$clientSecret) {
        [ValidateNotNullOrEmpty()][string]$GitHubUserName = $GitHubUserName
        [ValidateNotNullOrEmpty()][string]$GithubToken = $GithubToken = [AesCrypt]::GetString([securestring]$clientSecret)
        $encodedAuth = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($GitHubUserName):$($GithubToken)"))
        $web_session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
        [void]$web_session.Headers.Add('Authorization', "Basic $($encodedAuth)")
        [void]$web_session.Headers.Add('Accept', 'application/vnd.github.v3+json')
        [GitHub]::webSession = $web_session
        return $web_session
      }
      static [void] SetToken() {
        [GitHub]::SetToken([AesCrypt]::GetString((Read-Host -Prompt "[GitHub] Paste/write your api token" -AsSecureString)), $(Read-Host -Prompt "[GitHub] Paste/write a Password to encrypt the token" -AsSecureString))
      }
      static [void] SetToken([string]$token, [securestring]$password) {
        if (![IO.File]::Exists([GitHub]::TokenFile)) { New-Item -Type File -Path ([GitHub]::TokenFile) -Force | Out-Null }
        [IO.File]::WriteAllText([GitHub]::TokenFile, [convert]::ToBase64String([AesCrypt]::Encrypt([system.Text.Encoding]::UTF8.GetBytes($token), $password)), [System.Text.Encoding]::UTF8);
      }
      static [securestring] GetToken() {
        $sectoken = $null; $session_pass = [AesCrypt]::GetSecureString('123');
        try {
          if ([GitHub]::IsInteractive) {
            if ([string]::IsNullOrWhiteSpace((Get-Content ([GitHub]::TokenFile) -ErrorAction Ignore))) {
              Write-Host "[GitHub] You'll need to set your api token first. This is a One-Time Process :)" -ForegroundColor Green
              [GitHub]::SetToken()
              Write-Host "[GitHub] Good, now let's use the api token :)" -ForegroundColor DarkGreen
            } elseif ([GitHub]::ValidateBase64String([IO.File]::ReadAllText([GitHub]::TokenFile))) {
              Write-Host "[GitHub] Encrypted token found in file: $([GitHub]::TokenFile)" -ForegroundColor DarkGreen
            } else {
              throw [System.Exception]::New("Unable to read token file!")
            }
            $session_pass = Read-Host -Prompt "[GitHub] Input password to use your token" -AsSecureString
          } else {
            #Fix: Temporary Workaround: Thisz a pat from one of my GitHub a/cs.It Can only read/write gists. Will expire on 1/1/2025. DoNot Abuse this or I'll take it down!!
            $et = "OOLqqov4ugMQAtFcWqbzRwNBD65uf9JOZ+jzx1RtcHAZtnKaq1zkIpBcuv1MQfOkvIr/V066Zgsaq5Gka+VhlbqhV8apm8zcQomYjYqLaECKAonFeeo9MqvaP1F2VLgXokrxD1M6weLwS7KC+dyvAgv10IEvLzWFMw=="
            [GitHub]::SetToken([convert]::ToBase64String([aescrypt]::Decrypt([convert]::FromBase64String($et), $session_pass)), $session_pass)
          }
          $sectoken = [AesCrypt]::GetSecureString([system.Text.Encoding]::UTF8.GetString(
              [AesCrypt]::Decrypt([Convert]::FromBase64String([IO.File]::ReadAllText([GitHub]::GetTokenFile())), $session_pass)
            )
          )
        } catch {
          throw $_
        }
        return $sectoken
      }
      static [PsObject] GetUserInfo([string]$UserName) {
        if ([string]::IsNullOrWhiteSpace([GitHub]::userName)) { [GitHub]::createSession() }
        $response = Invoke-RestMethod -Uri "https://api.github.com/user/$UserName" -WebSession ([GitHub]::webSession) -Method Get -Verbose:$false
        return $response
      }
      static [PsObject] GetGist([uri]$Uri) {
        $l = [GitHub]::ParseGistUri($Uri)
        return [GitHub]::GetGist($l.Owner, $l.Id)
      }
      static [PsObject] GetGist([string]$UserName, [string]$GistId) {
        $t = [GitHub]::GetToken()
        if ($null -eq ([GitHub]::webSession)) {
          [GitHub]::webSession = $(if ($null -eq $t) {
              [GitHub]::createSession($UserName)
            } else {
              [GitHub]::createSession($UserName, $t)
            }
          )
        }
        if (![GitHub]::IsConnected()) {
          throw [System.Net.NetworkInformation.PingException]::new("PingException, PLease check your connection!");
        }
        if ([string]::IsNullOrWhiteSpace($GistId) -or $GistId -eq '*') {
          return Get-Gists -UserName $UserName -SecureToken $t
        }
        return Invoke-RestMethod -Uri "https://api.github.com/gists/$GistId" -WebSession ([GitHub]::webSession) -Method Get -Verbose:$false
      }
      Static [string] GetGistContent([string]$FileName, [uri]$GistUri) {
        return [GitHub]::GetGist($GistUri).files.$FileName.content
      }
      static [PsObject] CreateGist([string]$description, [array]$files) {
        $url = 'https://api.github.com/gists'
        $body = @{
          description = $description
          files       = @{}
        }
        foreach ($file in $files) {
          $body.files[$file.Name] = @{
            content = $file.Content
          }
        }
        $response = Invoke-RestMethod -Uri $url -WebSession ([GitHub]::webSession) -Method Post -Body ($body | ConvertTo-Json) -Verbose:$false
        return $response
      }
      static [PsObject] UpdateGist([GistFile]$gist, [string]$NewContent) {
        return ''
      }
      static [string] GetTokenFile() {
        if (![IO.File]::Exists([GitHub]::TokenFile)) {
          [GitHub]::TokenFile = [IO.Path]::Combine([GitHub]::Get_dataPath('Github', 'clicache'), "token");
        }
        return [GitHub]::TokenFile
      }
      static [GistFile] ParseGistUri([uri]$GistUri) {
        $res = $null; $ogs = $GistUri.OriginalString
        $IsRawUri = $ogs.Contains('/raw/') -and $ogs.Contains('gist.githubusercontent.com')
        $seg = $GistUri.Segments
        $res = $(if ($IsRawUri) {
            $name = $seg[-1]
            $rtri = 'https://gist.github.com/{0}{1}' -f $seg[1], $seg[2]
            $rtri = $rtri.Remove($rtri.Length - 1)
            $info = [GitHub]::GetGist([uri]::new($rtri))
            $file = $info.files."$name"
            [PsCustomObject]@{
              language = $file.language
              IsPublic = $info.IsPublic
              raw_url  = $file.raw_url
              Owner    = $info.owner.login
              type     = $file.type
              filename = $name
              size     = $file.size
              Id       = $seg[2].Replace('/', '')
            }
          } else {
            # $info = [GitHub]::GetGist($GistUri)
            [PsCustomObject]@{
              language = ''
              IsPublic = $null
              raw_url  = ''
              Owner    = $seg[1].Split('/')[0]
              type     = ''
              filename = ''
              size     = ''
              Id       = $seg[-1]
            }
          }
        )
        return [GistFile]::New($res)
      }
      static [PsObject] GetUserRepositories() {
        if ($null -eq [GitHub]::webSession) { [Github]::createSession() }
        $response = Invoke-RestMethod -Uri 'https://api.github.com/user/repos' -WebSession ([GitHub]::webSession) -Method Get -Verbose:$false
        return $response
      }
      static [psobject] ParseLink([string]$text, [bool]$throwOnFailure) {
        [ValidateNotNullOrEmpty()][string]$text = $text
        $uri = $text -as 'Uri'; if ($uri -isnot [Uri] -and $throwOnFailure) {
          throw [System.InvalidOperationException]::New("Could not create uri from text '$text'.")
        }; $Scheme = $uri.Scheme
        if ([regex]::IsMatch($text, '^(\/[a-zA-Z0-9_-]+)+|([a-zA-Z]:\\(((?![<>:"\/\\|?*]).)+\\?)*((?![<>:"\/\\|?*]).)+)$')) {
          if ($text.ToCharArray().Where({ $_ -in [IO.Path]::InvalidPathChars }).Count -eq 0) {
            $Scheme = 'file'
          } else {
            Write-Debug "'$text' has invalidPathChars in it !" -Debug
          }
        }
        $IsValid = $Scheme -in @('file', 'https')
        $IsGistUrl = [Regex]::IsMatch($text, 'https?://gist\.github\.com/\w+/[0-9a-f]+')
        $OutptObject = [pscustomobject]@{
          FullName = $text
          Scheme   = [PSCustomObject]@{
            Name      = $Scheme
            IsValid   = $IsValid
            IsGistUrl = $IsGistUrl
          }
        }
        return $OutptObject
      }
      static [string] Get_Host_Os() {
        # Should return one of these: [Enum]::GetNames([System.PlatformID])
        return $(if ($(Get-Variable IsWindows -Value)) { "Windows" }elseif ($(Get-Variable IsLinux -Value)) { "Linux" }elseif ($(Get-Variable IsMacOS -Value)) { "macOS" }else { "UNKNOWN" })
      }
      static [IO.DirectoryInfo] Get_dataPath([string]$appName, [string]$SubdirName) {
        $_Host_OS = [GitHub]::Get_Host_Os()
        $dataPath = if ($_Host_OS -eq 'Windows') {
          [System.IO.DirectoryInfo]::new([IO.Path]::Combine($Env:HOME, "AppData", "Roaming", $appName, $SubdirName))
        } elseif ($_Host_OS -in ('Linux', 'MacOs')) {
          [System.IO.DirectoryInfo]::new([IO.Path]::Combine((($env:PSModulePath -split [IO.Path]::PathSeparator)[0] | Split-Path | Split-Path), $appName, $SubdirName))
        } elseif ($_Host_OS -eq 'Unknown') {
          try {
            [System.IO.DirectoryInfo]::new([IO.Path]::Combine((($env:PSModulePath -split [IO.Path]::PathSeparator)[0] | Split-Path | Split-Path), $appName, $SubdirName))
          } catch {
            Write-Warning "Could not resolve chat data path"
            Write-Warning "HostOS = '$_Host_OS'. Could not resolve data path."
            [System.IO.Directory]::CreateTempSubdirectory(($SubdirName + 'Data-'))
          }
        } else {
          throw [InvalidOperationException]::new('Could not resolve data path. Get_Host_OS FAILED!')
        }
        if (!$dataPath.Exists) { [GitHub]::Create_Dir($dataPath) }
        return $dataPath
      }
      static [void] Create_Dir([string]$Path) {
        [GitHub]::Create_Dir([System.IO.DirectoryInfo]::new($Path))
      }
      static [void] Create_Dir([System.IO.DirectoryInfo]$Path) {
        [ValidateNotNullOrEmpty()][System.IO.DirectoryInfo]$Path = $Path
        $nF = @(); $p = $Path; while (!$p.Exists) { $nF += $p; $p = $p.Parent }
        [Array]::Reverse($nF); $nF | ForEach-Object { $_.Create(); Write-Verbose "Created $_" }
      }
      static [bool] ValidateBase64String([string]$base64) {
        return $(try { [void][Convert]::FromBase64String($base64); $true } catch { $false })
      }
      static [bool] IsConnected() {
        if (![bool]("System.Net.NetworkInformation.Ping" -as 'type')) { Add-Type -AssemblyName System.Net.NetworkInformation };
        $cs = $null; $re = @{ true = @{ m = "Success"; c = "Green" }; false = @{ m = "Failed"; c = "Red" } }
        Write-Host "[Github] Testing Connection ... " -ForegroundColor Blue -NoNewline
        try {
          [System.Net.NetworkInformation.PingReply]$PingReply = [System.Net.NetworkInformation.Ping]::new().Send("github.com");
          $cs = $PingReply.Status -eq [System.Net.NetworkInformation.IPStatus]::Success
        } catch [System.Net.Sockets.SocketException], [System.Net.NetworkInformation.PingException] {
          $cs = $false
        } catch {
          $cs = $false;
          Write-Error $_
        }
        $re = $re[$cs.ToString()]
        Write-Host $re.m -ForegroundColor $re.c
        return $cs
      }
    }

    class GistFile {
      [ValidateNotNullOrEmpty()][string]$Name # with extention
      [string]$language
      [string]$raw_url
      [bool]$IsPublic
      [string]$Owner
      [string]$type
      [string]$Id
      [int]$size

      GistFile([string]$filename) {
        $this.Name = $filename
      }
      GistFile([psobject]$InputObject) {
        $this.language = $InputObject.language
        $this.IsPublic = $InputObject.IsPublic
        $this.raw_url = $InputObject.raw_url
        $this.Owner = $InputObject.Owner
        $this.type = $InputObject.type
        $this.Name = $InputObject.filename
        $this.size = $InputObject.size
        $this.Id = $InputObject.Id
      }


      [string] ShowFileInfo() {
        return "File: $($this.Name)"
      }
    }

    class Gist {
      [uri] $Uri
      [string] $Id
      [string] $Owner
      [string] $Description
      [bool] $IsPublic
      [GistFile[]] $Files = @()

      Gist() {}
      Gist([string]$Name) {
        $this.AddFile([GistFile]::new($Name))
      }
      [psobject] Post() {
        $gisfiles = @()
        $this.Files.Foreach({
            $gisfiles += @{
              $_.Name = @{
                content = $_.Content
              }
            }
          }
        )
        $data = @{
          files       = $gisfiles
          description = $this.Description
          public      = $this.IsPublic
        } | ConvertTo-Json

        Write-Verbose ($data | Out-String)
        Write-Verbose "[PROCESS] Posting to https://api.github.com/gists"
        $invokeParams = @{
          Method      = 'Post'
          Uri         = "https://api.github.com/gists"
          WebSession  = [GitHub]::webSession
          Body        = $data
          ContentType = 'application/json'
        }
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
        $r = Invoke-RestMethod @invokeParams
        $r = $r | Select-Object @{Name = "Url"; Expression = { $_.html_url } }, Description, Public, @{Name = "Created"; Expression = { $_.created_at -as [datetime] } }
        return $r
      }
      [void] AddFile([GistFile]$file) {
        $this.Files += $file
      }
      [string] ShowInfo() {
        $info = "Gist ID: $($this.Id)"
        $info += "`nDescription: $($this.Description)"
        $info += "`nFiles:"
        foreach ($file in $this.Files.Values) {
          $info += "`n - $($file.ShowFileInfo())"
        }
        return $info
      }
    }
    class Shuffl3r {
      static [Byte[]] Combine([Byte[]]$Bytes, [Byte[]]$Nonce, [securestring]$Passwod) {
        return [Shuffl3r]::Combine($bytes, $Nonce, [AesCrypt]::GetString($Passwod))
      }
      static [Byte[]] Combine([Byte[]]$Bytes, [Byte[]]$Nonce, [string]$Passw0d) {
        # if ($Bytes.Length -lt 16) { throw [InvalidArgumentException]::New('Bytes', 'Input bytes.length should be > 16. ie: $minLength = 17, since the common $nonce length is 16') }
        if ($bytes.Length -lt ($Nonce.Length + 1)) {
          Write-Debug "Bytes.Length = $($Bytes.Length) but Nonce.Length = $($Nonce.Length)" -Debug
          throw [System.ArgumentOutOfRangeException]::new("Nonce", 'Make sure $Bytes.length > $Nonce.Length')
        }
        if ([string]::IsNullOrWhiteSpace($Passw0d)) { throw [System.ArgumentNullException]::new('$Passw0d') }
        [int[]]$Indices = [int[]]::new($Nonce.Length);
        Set-Variable -Name Indices -Scope local -Visibility Public -Option ReadOnly -Value ([Shuffl3r]::GenerateIndices($Nonce.Length, $Passw0d, $bytes.Length));
        [Byte[]]$combined = [Byte[]]::new($bytes.Length + $Nonce.Length);
        for ([int]$i = 0; $i -lt $Indices.Length; $i++) {
          $combined[$Indices[$i]] = $Nonce[$i]
        }
        $i = 0; $ir = (0..($combined.Length - 1)) | Where-Object { $_ -NotIn $Indices };
        foreach ($j in $ir) { $combined[$j] = $bytes[$i]; $i++ }
        return $combined
      }
      static [array] Split([Byte[]]$ShuffledBytes, [securestring]$Passwod, [int]$NonceLength) {
        return [Shuffl3r]::Split($ShuffledBytes, [AesCrypt]::GetString($Passwod), [int]$NonceLength);
      }
      static [array] Split([Byte[]]$ShuffledBytes, [string]$Passw0d, [int]$NonceLength) {
        if ($null -eq $ShuffledBytes) { throw [System.ArgumentNullException]::new('$ShuffledBytes') }
        if ([string]::IsNullOrWhiteSpace($Passw0d)) { throw [System.ArgumentNullException]::new('$Passw0d') }
        [int[]]$Indices = [int[]]::new([int]$NonceLength);
        Set-Variable -Name Indices -Scope local -Visibility Private -Option ReadOnly -Value ([Shuffl3r]::GenerateIndices($NonceLength, $Passw0d, ($ShuffledBytes.Length - $NonceLength)));
        $Nonce = [Byte[]]::new($NonceLength);
        $bytes = [Byte[]]$((0..($ShuffledBytes.Length - 1)) | Where-Object { $_ -NotIn $Indices } | Select-Object *, @{l = 'bytes'; e = { $ShuffledBytes[$_] } }).bytes
        for ($i = 0; $i -lt $NonceLength; $i++) { $Nonce[$i] = $ShuffledBytes[$Indices[$i]] };
        return ($bytes, $Nonce)
      }
      static hidden [int[]] GenerateIndices([int]$Count, [string]$randomString, [int]$HighestIndex) {
        if ($HighestIndex -lt 3 -or $Count -ge $HighestIndex) { throw [System.ArgumentOutOfRangeException]::new('$HighestIndex >= 3 is required; and $Count should be less than $HighestIndex') }
        if ([string]::IsNullOrWhiteSpace($randomString)) { throw [System.ArgumentNullException]::new('$randomString') }
        [Byte[]]$hash = [System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes([string]$randomString))
        [int[]]$indices = [int[]]::new($Count)
        for ($i = 0; $i -lt $Count; $i++) {
          [int]$nextIndex = [Convert]::ToInt32($hash[$i] % $HighestIndex)
          while ($indices -contains $nextIndex) {
            $nextIndex = ($nextIndex + 1) % $HighestIndex
          }
          $indices[$i] = $nextIndex
        }
        return $indices
      }
    }
    # Custom AES Galois/Counter Mode implementation
    class AesCrypt {
      static hidden [string] $caller
      static [ValidateNotNull()][EncryptionScope] $EncryptionScope
      static [byte[]] GetDerivedSalt([securestring]$password) {
        $rfc2898 = $null; $s4lt = $null; [byte[]]$s6lt = if ([AesCrypt]::EncryptionScope.ToString() -eq "Machine") {
          [System.Text.Encoding]::UTF8.GetBytes([AesCrypt]::GetUniqueMachineId())
        } else {
          [convert]::FromBase64String("qmkmopealodukpvdiexiianpnnutirid")
        }
        Set-Variable -Name password -Scope Local -Visibility Private -Option Private -Value $password;
        Set-Variable -Name s4lt -Scope Local -Visibility Private -Option Private -Value $s6lt;
        Set-Variable -Name rfc2898 -Scope Local -Visibility Private -Option Private -Value $([System.Security.Cryptography.Rfc2898DeriveBytes]::new($password, $s6lt));
        Set-Variable -Name s4lt -Scope Local -Visibility Private -Option Private -Value $($rfc2898.GetBytes(16));
        return $s4lt
      }
      static [byte[]] Encrypt([byte[]]$bytes) {
        return [AesCrypt]::Encrypt($bytes, [AesCrypt]::GetPassword());
      }
      static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$Password) {
        [byte[]]$_salt = [AesCrypt]::GetDerivedSalt($Password)
        return [AesCrypt]::Encrypt($bytes, $Password, $_salt);
      }
      static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt) {
        return [AesCrypt]::Encrypt($bytes, $Password, $Salt, $null, $null, 1);
      }
      static [string] Encrypt([string]$text, [SecureString]$Password, [int]$iterations) {
        return [convert]::ToBase64String([AesCrypt]::Encrypt([System.Text.Encoding]::UTF8.GetBytes("$text"), $Password, $iterations));
      }
      static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$Password, [int]$iterations) {
        [byte[]]$_salt = [AesCrypt]::GetDerivedSalt($Password)
        return [AesCrypt]::Encrypt($bytes, $Password, $_salt, $null, $null, $iterations);
      }
      static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt, [int]$iterations) {
        return [AesCrypt]::Encrypt($bytes, $Password, $Salt, $null, $null, $iterations);
      }
      static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$Password, [int]$iterations, [string]$Compression) {
        [byte[]]$_salt = [AesCrypt]::GetDerivedSalt($Password)
        return [AesCrypt]::Encrypt($bytes, $Password, $_salt, $null, $Compression, $iterations);
      }
      static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt, [byte[]]$associatedData, [int]$iterations) {
        return [AesCrypt]::Encrypt($bytes, $Password, $Salt, $associatedData, $null, $iterations);
      }
      static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt, [byte[]]$associatedData) {
        return [AesCrypt]::Encrypt($bytes, $Password, $Salt, $associatedData, $null, 1);
      }
      static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt, [byte[]]$associatedData, [string]$Compression, [int]$iterations) {
        [int]$IV_SIZE = 0; Set-Variable -Name IV_SIZE -Scope Local -Visibility Private -Option Private -Value 12
        [int]$TAG_SIZE = 0; Set-Variable -Name TAG_SIZE -Scope Local -Visibility Private -Option Private -Value 16
        [string]$Key = $null; Set-Variable -Name Key -Scope Local -Visibility Private -Option Private -Value $([convert]::ToBase64String([System.Security.Cryptography.Rfc2898DeriveBytes]::new([AesCrypt]::GetString($Password), $Salt, 10000, [System.Security.Cryptography.HashAlgorithmName]::SHA1).GetBytes(32)));
        [System.IntPtr]$th = [System.IntPtr]::new(0); if ([string]::IsNullOrWhiteSpace([AesCrypt]::caller)) { [AesCrypt]::caller = '[AesCrypt]' }
        Set-Variable -Name th -Scope Local -Visibility Private -Option Private -Value $([System.Runtime.InteropServices.Marshal]::StringToHGlobalAnsi($TAG_SIZE));
        try {
          $_bytes = $bytes;
          $aes = $null; Set-Variable -Name aes -Scope Local -Visibility Private -Option Private -Value $([ScriptBlock]::Create("[Security.Cryptography.AesGcm]::new([convert]::FromBase64String('$Key'))").Invoke());
          for ($i = 1; $i -lt $iterations + 1; $i++) {
            # if ($Protect) { $_bytes = [xconvert]::ToProtected($_bytes, $Salt, [EncryptionScope]::User) }
            # Generate a random IV for each iteration:
            [byte[]]$IV = $null; Set-Variable -Name IV -Scope Local -Visibility Private -Option Private -Value ([System.Security.Cryptography.Rfc2898DeriveBytes]::new([AesCrypt]::GetString($password), $salt, 1, [System.Security.Cryptography.HashAlgorithmName]::SHA1).GetBytes($IV_SIZE));
            $tag = [byte[]]::new($TAG_SIZE);
            $Encrypted = [byte[]]::new($_bytes.Length);
            [void]$aes.Encrypt($IV, $_bytes, $Encrypted, $tag, $associatedData);
            $_bytes = [Shuffl3r]::Combine([Shuffl3r]::Combine($Encrypted, $IV, $Password), $tag, $Password);
            Write-Debug "$([AesCrypt]::caller) [+] Encryption [$i/$iterations] ... Done"
          }
        } catch {
          throw $_
        } finally {
          [void][System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocAnsi($th);
          Remove-Variable IV_SIZE, TAG_SIZE, th -ErrorAction SilentlyContinue
        }
        if (![string]::IsNullOrWhiteSpace($Compression)) {
          $_bytes = [AesCrypt]::Compress($_bytes, $Compression);
        }
        return $_bytes
      }
      static [byte[]] Decrypt([byte[]]$bytes) {
        return [AesCrypt]::Decrypt($bytes, [AesCrypt]::GetPassword());
      }
      static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$Password) {
        [byte[]]$_salt = [AesCrypt]::GetDerivedSalt($Password)
        return [AesCrypt]::Decrypt($bytes, $Password, $_salt);
      }
      static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt) {
        return [AesCrypt]::Decrypt($bytes, $Password, $Salt, $null, $null, 1);
      }
      static [string] Decrypt([string]$text, [SecureString]$Password, [int]$iterations) {
        return [System.Text.Encoding]::UTF8.GetString([AesCrypt]::Decrypt([convert]::FromBase64String($text), $Password, $iterations));
      }
      static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$Password, [int]$iterations) {
        [byte[]]$_salt = [AesCrypt]::GetDerivedSalt($Password)
        return [AesCrypt]::Decrypt($bytes, $Password, $_salt, $null, $null, $iterations);
      }
      static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt, [int]$iterations) {
        return [AesCrypt]::Decrypt($bytes, $Password, $Salt, $null, $null, 1);
      }
      static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$Password, [int]$iterations, [string]$Compression) {
        [byte[]]$_salt = [AesCrypt]::GetDerivedSalt($Password)
        return [AesCrypt]::Decrypt($bytes, $Password, $_salt, $null, $Compression, $iterations);
      }
      static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt, [byte[]]$associatedData, [int]$iterations) {
        return [AesCrypt]::Decrypt($bytes, $Password, $Salt, $associatedData, $null, $iterations);
      }
      static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt, [byte[]]$associatedData) {
        return [AesCrypt]::Decrypt($bytes, $Password, $Salt, $associatedData, $null, 1);
      }
      static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$Password, [byte[]]$Salt, [byte[]]$associatedData, [string]$Compression, [int]$iterations) {
        [int]$IV_SIZE = 0; Set-Variable -Name IV_SIZE -Scope Local -Visibility Private -Option Private -Value 12
        [int]$TAG_SIZE = 0; Set-Variable -Name TAG_SIZE -Scope Local -Visibility Private -Option Private -Value 16
        [string]$Key = $null; Set-Variable -Name Key -Scope Local -Visibility Private -Option Private -Value $([convert]::ToBase64String([System.Security.Cryptography.Rfc2898DeriveBytes]::new([AesCrypt]::GetString($Password), $Salt, 10000, [System.Security.Cryptography.HashAlgorithmName]::SHA1).GetBytes(32)));
        [System.IntPtr]$th = [System.IntPtr]::new(0); if ([string]::IsNullOrWhiteSpace([AesCrypt]::caller)) { [AesCrypt]::caller = '[AesCrypt]' }
        Set-Variable -Name th -Scope Local -Visibility Private -Option Private -Value $([System.Runtime.InteropServices.Marshal]::StringToHGlobalAnsi($TAG_SIZE));
        try {
          $_bytes = if (![string]::IsNullOrWhiteSpace($Compression)) { [AesCrypt]::DeCompress($bytes, $Compression) } else { $bytes }
          $aes = [ScriptBlock]::Create("[Security.Cryptography.AesGcm]::new([convert]::FromBase64String('$Key'))").Invoke()
          for ($i = 1; $i -lt $iterations + 1; $i++) {
            # if ($UnProtect) { $_bytes = [xconvert]::ToUnProtected($_bytes, $Salt, [EncryptionScope]::User) }
            # Split the real encrypted bytes from nonce & tags then decrypt them:
                        ($b, $n1) = [Shuffl3r]::Split($_bytes, $Password, $TAG_SIZE);
                        ($b, $n2) = [Shuffl3r]::Split($b, $Password, $IV_SIZE);
            $Decrypted = [byte[]]::new($b.Length);
            $aes.Decrypt($n2, $b, $n1, $Decrypted, $associatedData);
            $_bytes = $Decrypted;
            Write-Debug "$([AesCrypt]::caller) [+] Decryption [$i/$iterations] ... Done"
          }
        } catch {
          if ($_.FullyQualifiedErrorId -eq "AuthenticationTagMismatchException") {
            Write-Host "$([AesCrypt]::caller) Wrong password" -ForegroundColor Yellow
          }
          throw $_
        } finally {
          [void][System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocAnsi($th);
          Remove-Variable IV_SIZE, TAG_SIZE, th -ErrorAction SilentlyContinue
        }
        return $_bytes
      }
      static [byte[]] Compress([byte[]]$Bytes) {
        return [AesCrypt]::Compress($Bytes, 'Gzip');
      }
      static [string] Compress([string]$Plaintext) {
        return [convert]::ToBase64String([AesCrypt]::Compress([System.Text.Encoding]::UTF8.GetBytes($Plaintext)));
      }
      static [byte[]] Compress([byte[]]$Bytes, [string]$Compression) {
        if (("$Compression" -as 'Compression') -isnot 'Compression') {
          Throw [System.InvalidCastException]::new("Compression type '$Compression' is unknown! Valid values: $([Enum]::GetNames([AesCrypt]) -join ', ')");
        }
        $outstream = [System.IO.MemoryStream]::new()
        $Comstream = switch ($Compression) {
          "Gzip" { New-Object System.IO.Compression.GzipStream($outstream, [System.IO.Compression.CompressionLevel]::Optimal) }
          "Deflate" { New-Object System.IO.Compression.DeflateStream($outstream, [System.IO.Compression.CompressionLevel]::Optimal) }
          "ZLib" { New-Object System.IO.Compression.ZLibStream($outstream, [System.IO.Compression.CompressionLevel]::Optimal) }
          Default { throw "Failed to Compress Bytes. Could Not resolve Compression!" }
        }
        [void]$Comstream.Write($Bytes, 0, $Bytes.Length); $Comstream.Close(); $Comstream.Dispose();
        [byte[]]$OutPut = $outstream.ToArray(); $outStream.Close()
        return $OutPut;
      }
      static [byte[]] DeCompress([byte[]]$Bytes) {
        return [AesCrypt]::DeCompress($Bytes, 'Gzip');
      }
      static [string] DeCompress([string]$Base64Text) {
        return [System.Text.Encoding]::UTF8.GetString([AesCrypt]::DeCompress([convert]::FromBase64String($Base64Text)));
      }
      static [byte[]] DeCompress([byte[]]$Bytes, [string]$Compression) {
        if (("$Compression" -as 'Compression') -isnot 'Compression') {
          Throw [System.InvalidCastException]::new("Compression type '$Compression' is unknown! Valid values: $([Enum]::GetNames([compression]) -join ', ')");
        }
        $inpStream = [System.IO.MemoryStream]::new($Bytes)
        $ComStream = switch ($Compression) {
          "Gzip" { New-Object System.IO.Compression.GzipStream($inpStream, [System.IO.Compression.CompressionMode]::Decompress); }
          "Deflate" { New-Object System.IO.Compression.DeflateStream($inpStream, [System.IO.Compression.CompressionMode]::Decompress); }
          "ZLib" { New-Object System.IO.Compression.ZLibStream($inpStream, [System.IO.Compression.CompressionMode]::Decompress); }
          Default { throw "Failed to DeCompress Bytes. Could Not resolve Compression!" }
        }
        $outStream = [System.IO.MemoryStream]::new();
        [void]$Comstream.CopyTo($outStream); $Comstream.Close(); $Comstream.Dispose(); $inpStream.Close()
        [byte[]]$OutPut = $outstream.ToArray(); $outStream.Close()
        return $OutPut;
      }
      static [string] GetUniqueMachineId() {
        $Id = [string]($Env:MachineId)
        $vp = (Get-Variable VerbosePreference).Value
        try {
          Set-Variable VerbosePreference -Value $([System.Management.Automation.ActionPreference]::SilentlyContinue)
          $sha256 = [System.Security.Cryptography.SHA256]::Create()
          $HostOS = $(if ($(Get-Variable PSVersionTable -Value).PSVersion.Major -le 5 -or $(Get-Variable IsWindows -Value)) { "Windows" }elseif ($(Get-Variable IsLinux -Value)) { "Linux" }elseif ($(Get-Variable IsMacOS -Value)) { "macOS" }else { "UNKNOWN" });
          if ($HostOS -eq "Windows") {
            if ([string]::IsNullOrWhiteSpace($Id)) {
              $machineId = Get-CimInstance -ClassName Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID
              Set-Item -Path Env:\MachineId -Value $([convert]::ToBase64String($sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($machineId))));
            }
            $Id = [string]($Env:MachineId)
          } elseif ($HostOS -eq "Linux") {
            # $Id = (sudo cat /sys/class/dmi/id/product_uuid).Trim() # sudo prompt is a nono
            # Lets use mac addresses
            $Id = ([string[]]$(ip link show | grep "link/ether" | awk '{print $2}') -join '-').Trim()
            $Id = [convert]::ToBase64String($sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Id)))
          } elseif ($HostOS -eq "macOS") {
            $Id = (system_profiler SPHardwareDataType | Select-String "UUID").Line.Split(":")[1].Trim()
            $Id = [convert]::ToBase64String($sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Id)))
          } else {
            throw "Error: HostOS = '$HostOS'. Could not determine the operating system."
          }
        } catch {
          throw $_
        } finally {
          $sha256.Clear(); $sha256.Dispose()
          Set-Variable VerbosePreference -Value $vp
        }
        return $Id
      }
      static [SecureString] GetSecureString([string]$String) {
        $SecureString = $null; Set-Variable -Name SecureString -Scope Local -Visibility Private -Option Private -Value ([System.Security.SecureString]::new());
        if (![string]::IsNullOrEmpty($String)) {
          $Chars = $String.toCharArray()
          ForEach ($Char in $Chars) {
            $SecureString.AppendChar($Char)
          }
        }
        $SecureString.MakeReadOnly();
        return $SecureString
      }
      static [string] GetString([System.Security.SecureString]$SecureString) {
        [string]$Pstr = [string]::Empty;
        [IntPtr]$zero = [IntPtr]::Zero;
        if ($null -eq $SecureString -or $SecureString.Length -eq 0) {
          return [string]::Empty;
        }
        try {
          Set-Variable -Name zero -Scope Local -Visibility Private -Option Private -Value ([System.Runtime.InteropServices.Marshal]::SecurestringToBSTR($SecureString));
          Set-Variable -Name Pstr -Scope Local -Visibility Private -Option Private -Value ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($zero));
        } finally {
          if ($zero -ne [IntPtr]::Zero) {
            [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($zero);
          }
        }
        return $Pstr;
      }
      static [securestring] GetPassword() {
        $_pass = if ([AesCrypt]::EncryptionScope.ToString() -eq "Machine") {
          [AesCrypt]::GetSecureString([AesCrypt]::GetUniqueMachineId())
        } else {
          $password = [SecureString]::new();
          [Console]::Write("Enter your password: ");
          while ($true) {
            [ConsoleKeyInfo]$key = [Console]::ReadKey($true);
            if ($key.Key -eq [ConsoleKey]::Enter) {
              break;
            }
            $password.AppendChar($key.KeyChar);
            [Console]::Write("*");
          }
          $password
        }
        [Console]::WriteLine();
        return $_pass
      }
      static [void] ValidateCompression([string]$Compression) {
        if ($Compression -notin ([Enum]::GetNames('Compression' -as 'Type'))) { Throw [System.InvalidCastException]::new("The name '$Compression' is not a valid [Compression]`$typeName.") };
      }
    }
  }

  process {
    $content = [string]::Empty
    if ($PSBoundParameters.Keys.Count -eq 1) {
      if ([GitHub]::ParseLink($FileName, $false).Scheme.IsValid) {
        $PrsdUrl = [GitHub]::ParseLink($FileName, $false)
        if ($PrsdUrl.Scheme.IsGistUrl) {
          $res = [GitHub]::GetGist([uri]::new($PrsdUrl.FullName))
          Write-Verbose "[GitHub] Selecting first file in the gist"
          $fn0 = ($res.files[0] | Get-Member -MemberType noteproperty).Name[0]
          $content = $res.files.$fn0.content
        } else {
          Write-Error "Please Provide a valid Gist Url"
        }
      }
    } else {
      $content = [GitHub]::GetGistContent($FileName, [uri]::new($GistUrl))
    }
  }

  end {
    return $content
  }
}