Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 113 additions & 161 deletions PSSlack/Public/Send-SlackFile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@

.FUNCTIONALITY
Slack

.LINK
https://api.slack.com/methods/files.getUploadURLExternal

.LINK
https://api.slack.com/methods/files.completeUploadExternal
#>

[cmdletbinding(DefaultParameterSetName = 'Content')]
Expand Down Expand Up @@ -89,175 +95,121 @@
)
process
{
if ($Content) {
$body = @{}
switch ($psboundparameters.keys) {
'Content' {$body.content = $content}
'Channel' {$body.channels = $Channel -join ", " }
'Thread' {$body.thread_ts = $Thread}
'FileName' {$body.filename = $FileName}
'Title' {$body.Title = $Title}
'Comment' {$body.initial_comment = $Comment}
'FileType' {$body.filetype = $FileType}
}
Write-Verbose "Send-SlackApi -Body $($body | Format-List | Out-String)"
$Params = @{
Method = 'files.upload'
Body = $Body
Token = $Token
ForceVerbose = $ForceVerbose
}
$response = Send-SlackApi @Params
} else {

$fileName = (Split-Path -Path $Path -Leaf)
$uri = 'https://slack.com/api/files.upload'

if ($IsCoreCLR) {
# PowerShell Core implementation

$multipartContent = [System.Net.Http.MultipartFormDataContent]::new()

# Add file contents
$fileHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data')
$fileHeader.Name = 'file'
$fileHeader.FileName = $FileName
$fileStream = [System.IO.FileStream]::new($Path, [System.IO.FileMode]::Open)
$fileContent = [System.Net.Http.StreamContent]::new($fileStream)
$fileContent.Headers.ContentDisposition = $fileHeader
$fileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('multipart/form-data')
$multipartContent.Add($fileContent)

# Add token
$tokenHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data')
$tokenHeader.Name = 'token'
$tokenContent = [System.Net.Http.StringContent]::new($token)
$tokenContent.Headers.ContentDisposition = $tokenHeader
$multipartContent.Add($tokenContent)
Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)"
if(-not $Token) {
throw 'Please supply a Slack Api Token with Set-SlackApiToken.'
}
$secureToken = ConvertTo-SecureString -String $Token -AsPlainText -Force

switch ($psboundparameters.keys) {
'Channel' {
# Add channel
$channelHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data')
$channelHeader.Name = 'channels'
$channelContent = [System.Net.Http.StringContent]::new(($Channel -join ', '))
$channelContent.Headers.ContentDisposition = $channelHeader
$multipartContent.Add($channelContent)
}
'Thread' {
# Add Thread
$threadHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data')
$threadHeader.Name = 'thread_ts'
$threadContent = [System.Net.Http.StringContent]::new($Thread)
$threadContent.Headers.ContentDisposition = $threadHeader
$multipartContent.Add($threadContent)
}
'FileName' {
# Add file name
$filenameHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data')
$filenameHeader.Name = 'filename'
$filenameContent = [System.Net.Http.StringContent]::new($FileName)
$filenameContent.Headers.ContentDisposition = $filenameHeader
$multipartContent.Add($filenameContent)
}
'Title' {
# Add title
$titleHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data')
$titleHeader.Name = 'title'
$titleContent = [System.Net.Http.StringContent]::new($Title)
$titleContent.Headers.ContentDisposition = $titleHeader
$multipartContent.Add($titleContent)
}
'Comment' {
# Add comment
$commentHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data')
$commentHeader.Name = 'initial_comment'
$commentContent = [System.Net.Http.StringContent]::new($Comment)
$commentContent.Headers.ContentDisposition = $commentHeader
$multipartContent.Add($commentContent)
}
}
# File name and length
if($Content) {
$contentBytes = [System.Text.Encoding]::UTF8.GetBytes($Content)
$fileLength = $contentBytes.Length
}
else {
if([string]::IsNullOrWhiteSpace($FileName)) {
$FileName = Split-Path -Path $Path -Leaf
}
$fileLength = (Get-Item -Path $Path).Length
}

try {
$response = Invoke-RestMethod -Uri $uri -Method Post -Body $multipartContent
}
catch [System.Net.WebException] {
Write-Error( "Rest call failed for $uri`: $_" )
throw $_
}
finally {
$fileStream.Close()
}
# Step 1: Get upload URL
$uploadRequest = @{
filename = $FileName
length = $fileLength
}
# Potential future enhancement: alt_text support
# if($AltText) {
# $uploadRequest['alt_text'] = $AltText
# }
if($FileType) {
$uploadRequest['snippet_type'] = $FileType
}
$getUrlParams = @{
Body = $uploadRequest
ContentType = 'application/x-www-form-urlencoded'
Method = 'POST'
Uri = 'https://slack.com/api/files.getUploadURLExternal'
Verbose = $ForceVerbose.IsPresent
}
if($IsCoreCLR) {
$getUrlParams['Authentication'] = 'Bearer'
$getUrlParams['Token'] = $secureToken
}
else {
$getUrlParams['Headers'] = @{ Authorization = "Bearer $Token" }
}
$uploadUrlResponse = Invoke-RestMethod @getUrlParams
if($null -ne $uploadUrlResponse -and $false -eq $uploadUrlResponse.ok) {
$uploadUrlResponse | Parse-SlackError
}
else {
# Step 2: Upload content or file to the provided URL
$uploadParams = @{
ContentType = 'application/octet-stream'
Method = 'POST'
Uri = $uploadUrlResponse.upload_url
Verbose = $ForceVerbose.IsPresent
}
if($Content) {
$uploadParams['Body'] = $contentBytes
}
else {
# Legacy Windows PowerShell implementation

$LF = "`r`n"
$readFile = [System.IO.File]::ReadAllBytes($Path)
$enc = [System.Text.Encoding]::GetEncoding("iso-8859-1")
$fileEnc = $enc.GetString($readFile)
$boundary = [System.Guid]::NewGuid().ToString()

$bodyLines =
"--$boundary$LF" +
"Content-Disposition: form-data; name=`"file`"; filename=`"$fileName`"$LF" +
"Content-Type: 'multipart/form-data'$LF$LF" +
"$fileEnc$LF" +
"--$boundary$LF" +
"Content-Disposition: form-data; name=`"token`"$LF" +
"Content-Type: 'multipart/form-data'$LF$LF" +
"$token$LF"

$uploadParams['InFile'] = $Path
}
# The upload_url is pre-signed, so no auth token is needed
$uploadResponse = Invoke-RestMethod @uploadParams
if($null -ne $uploadResponse -and $false -eq $uploadResponse.ok) {
$uploadResponse | Parse-SlackError
}

switch ($psboundparameters.keys) {
'Channel' {$bodyLines +=
("--$boundary$LF" +
"Content-Disposition: form-data; name=`"channels`"$LF" +
"Content-Type: multipart/form-data$LF$LF" +
($Channel -join ", ") + $LF)}
'Thread' {$bodyLines +=
("--$boundary$LF" +
"Content-Disposition: form-data; name=`"thread_ts`"$LF" +
"Content-Type: multipart/form-data$LF$LF" +
"$Thread$LF")}
'FileName' {$bodyLines +=
("--$boundary$LF" +
"Content-Disposition: form-data; name=`"filename`"$LF" +
"Content-Type: multipart/form-data$LF$LF" +
"$FileName$LF")}
'Title' {$bodyLines +=
("--$boundary$LF" +
"Content-Disposition: form-data; name=`"title`"$LF" +
"Content-Type: multipart/form-data$LF$LF" +
"$Title$LF")}
'Comment' {$bodyLines +=
("--$boundary$LF" +
"Content-Disposition: form-data; name=`"initial_comment`"$LF" +
"Content-Type: multipart/form-data$LF$LF" +
"$Comment$LF")}
}
$bodyLines += "--$boundary--$LF"
try {
$Params = @{
Uri = $uri
Method = 'Post'
ContentType = "multipart/form-data; boundary=`"$boundary`""
Body = $bodyLines
}
if(-not $ForceVerbose) {
$Params.Add('Verbose', $False)
# Step 3: Complete the upload
$completeRequest = @{
files = @(
@{
id = $uploadUrlResponse.file_id
}
if($ForceVerbose) {
$Params.Add('Verbose', $true)
}
$response = Invoke-RestMethod @Params
)
}
if($Comment) {
$completeRequest['initial_comment'] = $Comment
# Potential future enhancement: add support for custom message blocks
}
if($Title) {
$completeRequest.files[0]['title'] = $Title
}
if($Thread) {
# thread_ts requires a single channel_id when completing upload in a thread
if($Channel) {
$completeRequest['channel_id'] = $Channel | Where-Object { $_ } | Select-Object -First 1
}
catch [System.Net.WebException] {
Write-Error( "Rest call failed for $uri`: $_" )
throw $_
$completeRequest['thread_ts'] = $Thread
}
else {
# If not targeting a thread, multiple channels can be specified
if($Channel) {
$completeRequest['channels'] = ($Channel | Where-Object { $_ } | Select-Object -Unique) -join ','
}
}
$completeParams = @{
Body = $completeRequest | ConvertTo-Json -Depth 10
ContentType = 'application/json; charset=utf-8'
Method = 'POST'
Uri = 'https://slack.com/api/files.completeUploadExternal'
Verbose = $ForceVerbose.IsPresent
}
if($IsCoreCLR) {
$completeParams['Authentication'] = 'Bearer'
$completeParams['Token'] = $secureToken
}
else {
$completeParams['Headers'] = @{ Authorization = "Bearer $Token" }
}
$response = Invoke-RestMethod @completeParams
if($null -ne $response -and $false -eq $response.ok) {
$response | Parse-SlackError
}
$response
}
$response
}
}
}