Types/OpenPackage.Source/AtProtocol.ps1
|
<# .SYNOPSIS Gets at protocol data .DESCRIPTION Gets data from the at protocol. .EXAMPLE Get-OpenPackage at://mrpowershell.com/app.bsky.actor.profile #> param( [Parameter(Mandatory)] [string[]] $AtUri, # A list of file wildcards to include. [Parameter(ValueFromPipelineByPropertyName)] [SupportsWildcards()] [string[]] $Include, # A list of file wildcards to exclude. [Parameter(ValueFromPipelineByPropertyName)] [SupportsWildcards()] [string[]] $Exclude, # The base path within the package. # Content should be added beneath this base path. [string] $BasePath = '/', # A content type map. # This maps extensions and URIs to a content type. [Collections.IDictionary] $TypeMap = $( ([PSCustomObject]@{PSTypeName='OpenPackage.ContentTypeMap'}).TypeMap ), # The compression option. [IO.Packaging.CompressionOption] [Alias('CompressionLevel')] $CompressionOption = 'Superfast', # The personal data server. This is used in At Protocol requests. [string] $pds = "https://bsky.social", # The current package [IO.Packaging.Package] $Package, # The batch size [ValidateRange(1,100)] [int] $BatchSize = 100, # Any additional headers to pass into a web request. [Alias('Header')] [Collections.IDictionary] $Headers, # The number of records to get [long] $First, # If number of records to skip [long] $Skip ) if (-not $package) { $memoryStream = [IO.MemoryStream]::new() $package = [IO.Packaging.Package]::Open($memoryStream, 'OpenOrCreate', 'ReadWrite') Add-Member -InputObject $package NoteProperty MemoryStream $memoryStream -Force } if (-not $this) {$this = $package} $sources = [PSCustomObject]@{PSTypeName='OpenPackage.Source'} filter packAtProtoRecord { # Declare a package uri for the segment. # (make sure to switch did colons to something else, so that the files can unpack cleanly regardless of OS) $currentPackageUri = "/$($atMatch.did -replace ':','_')/$($matches.type)/$($matches.rkey).json" # If the part exists, if ($Package.PartExists($currentPackageUri)) { $Package.DeletePart($currentPackageUri) # recreate it. if (-not $?) { return } } $atPart = $Package.CreatePart($currentPackageUri, 'application/json', $CompressionOption) if (-not $atPart) { continue } # Get the stream. $atStream = $atPart.GetStream() if (-not $atStream) { continue } # Turn our message into json, and get the bytes. $atJsonBytes = $outputEncoding.GetBytes( ($atRecord | ConvertTo-Json -Depth 100) ) # Then write them to the stream, $atStream.Write($atJsonBytes, 0, $atJsonBytes.Count) # clean up, $atStream.Close() $atStream.Dispose() } foreach ($at in $AtUri) { # and declare a pattern to pick apart an at uri $atPattern = 'at://(?<did>[^/]+)/(?<type>[^/]+)(?:/(?<rkey>.+?$))?' # If this does not match the pattern or we don't have a type, we are done. if ($at -notmatch $atPattern -or -not $matches.type) { return } # Store our match information before anything else needs to `-match`. $atMatch = [Ordered]@{} + $Matches # Create a package in memory if (-not $package.PackageProperties.Identifier) { $package.PackageProperties.Identifier = $atMatch.did } # If we have a type and rkey, we are after a single record. if ($atMatch.type -and $atMatch.rkey) { $atRecord = $sources.AtRecord($atMatch.did, $atMatch.type, $atMatch.rkey, $pds) if (-not $atRecord) { continue } packAtProtoRecord } elseif ($atMatch.type -match '\.') { # If there are dots in the type, it is a collection foreach ($atRecord in $sources.AtType( $atMatch.did, $atMatch.type, $BatchSize, $First, $Skip, $pds )) { # If the uri is not an at uri if ($atRecord.uri -notmatch $atPattern) { # continue to the next record continue } packAtProtoRecord } } else { try { $atBlob = $sources.AtBlob($matches.did, $matches.type) $atContentType = $atBlob.Headers.'Content-Type' if (-not $atContentType) { continue } $currentPackageUri = "/$($matches.did -replace ':','_')/$($matches.type).$(@($atContentType -split '[/\+]')[-1])" $blobPart = $Package.CreatePart($currentPackageUri, $atContentType, $CompressionOption) $blobStream = $blobPart.GetStream() $memoryStream = if ($atBlob.Content -is [byte[]]) { [IO.MemoryStream]::new($atBlob.Content) } else { [IO.MemoryStream]::new([Text.Encoding]::UTF8.GetBytes($atBlob.Content)) } $memoryStream.CopyTo($blobStream) $memoryStream.Close() $null = $memoryStream.DisposeAsync() $blobStream.Close() $null = $blobStream.DisposeAsync() } catch { Write-Debug "Unable to get $at : $_" continue } } } # Only return a package if it is not empty. if (@($package.GetParts()).Length) { return $Package } |