BluebirdPS.psm1
using namespace System.Management.Automation using namespace Collections.ObjectModel using namespace System.Collections using namespace Microsoft.PowerShell.Commands # -------------------------------------------------------------------------------------------------- #region set base path variables $ResourcesPath = Join-Path -Path $PSScriptRoot -ChildPath 'resources' if ($IsWindows) { $DefaultSavePath = Join-Path -Path $env:USERPROFILE -ChildPath '.BluebirdPS' } else { $DefaultSavePath = Join-Path -Path $env:HOME -ChildPath '.BluebirdPS' } #endregion #region Authentication variables and setup [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $OAuthTokenSavePath = Join-Path -Path $DefaultSavePath -ChildPath 'twittercred.sav' [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $OAuth = @{ ApiKey = $null ApiSecret = $null AccessToken = $null AccessTokenSecret = $null BearerToken = $null } #endregion #region Configuration variables and setup [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $ConfigurationSavePath = Join-Path -Path $DefaultSavePath -ChildPath 'TwitterConfiguration.json' [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $TwitterConfiguration = $null [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $TwitterConfigurationRefreshDate = $null #endregion #region [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $LanguagesSavePath = Join-Path -Path $DefaultSavePath -ChildPath 'TwitterLanguages.json' [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $TwitterLanguages = $null #endregion #region ErrorMapping variables and setup [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $ErrorMappingPath = Join-Path -Path $ResourcesPath -ChildPath 'TwitterErrorCodeExceptions.json' [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $TwitterErrorMapping = $null #endregion #region other variables [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $ApiEndpointsPath = Join-Path -Path $ResourcesPath -ChildPath 'TwitterApiEndpoints.json' [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $RateLimitWarning = $false [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $TwitterHistoryList = [System.Collections.Generic.List[object]]::new() #endregion #region source: \classes\OAuthParameters.Class.psm1 class OAuthParameters { [string]$HttpMethod [String]$BaseUri [hashtable]$Query [System.UriBuilder]$UriBuilder [string]$UnescapedQueryString [string]$EscapedQueryString [object]$Body [hashtable]$Form # ------------------------------------------------------------------------------------------------------------------ # constructor for OAuthParameters # ------------------------------------------------------------------------------------------------------------------ OAuthParameters ([string] $HttpMethod, [string] $BaseUri, [hashtable] $Query) { $this.SetHttpMethod($HttpMethod) $this.SetBaseUri($BaseUri) $this.SetQuery($Query) } OAuthParameters ([string] $HttpMethod, [string] $BaseUri) { $this.SetHttpMethod($HttpMethod) $this.SetBaseUri($BaseUri) } # ------------------------------------------------------------------------------------------------------------------ # methods # ------------------------------------------------------------------------------------------------------------------ # generates the required OAuth signature string # Nonce and Timestamp are variables in order to support testing [string] GetOAuthSignatureString ( [string]$ApiKey,[string]$ApiSecret, [string]$AccessToken,[string]$AccessTokenSecret, [string]$Nonce,[string]$Timestamp ) { if ([System.String]::IsNullOrEmpty($Nonce)) { $Nonce = $this.GetNonce() } if ([System.String]::IsNullOrEmpty($Timestamp)) { $Timestamp = $this.GetUnixTime() } # get parameter string $ParameterString = $this.GetParameterString($ApiKey,$AccessToken,$Nonce,$Timestamp) # create signature base $SignatureBaseString = '{0}&{1}&{2}' -f $this.HttpMethod, [System.Uri]::EscapeDataString($this.BaseUri), [System.Uri]::EscapeDataString($ParameterString) # set signing key $SigningKey = '{0}&{1}' -f [System.Uri]::EscapeDataString($ApiSecret), [System.Uri]::EscapeDataString($AccessTokenSecret) # generate OAuth signature $SignatureMethod = [System.Security.Cryptography.HMACSHA1]::new() $SignatureMethod.Key = [System.Text.Encoding]::ASCII.GetBytes($SigningKey) $OAuthSignature = [System.Convert]::ToBase64String( $SignatureMethod.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($SignatureBaseString)) ) # add OAuth signature to header $OAuthHeader = $this.GetOAuthHeader($ApiKey,$AccessToken,$Nonce,$Timestamp) $OAuthHeader.Add('oauth_signature',$OAuthSignature) # escape OAuth key $OAuthHeaderString = $OAuthHeader.GetEnumerator() | Sort-Object Name | ForEach-Object { '{0}="{1}"' -f $_.Key,[System.Uri]::EscapeDataString($_.Value) } # build OAuth sstring $OAuthString = [System.Text.StringBuilder]::new() $null = $OAuthString.Append('OAuth ') $null = $OAuthString.Append(($OAuthHeaderString -join ', ')) return $OAuthString.ToString() } # used to update Query, UnescapedQueryString, and UriBuilder # required for cursoring and paging [void] SetQuery($Query) { $this.Query = $Query $UriQueryString = [System.Collections.Generic.List[string]]::new() $this.Query.Keys | Sort-Object | Foreach-Object { $null = $UriQueryString.Add(('{0}={1}' -f $_,$this.Query[$_])) } $this.UnescapedQueryString = $UriQueryString -join '&' $this.UriBuilder = ($null -ne $this.Query) ? '{0}?{1}' -f $this.BaseUri,$this.UnescapedQueryString : $this.BaseUri $this.EscapedQueryString = $this.UriBuilder.Uri.Query.Substring(1) } # provide simple way to include text output [string] ToString() { $QueryObject = [PsCustomObject][ordered]@{} foreach ($Key in ($this.Query.Keys | Sort-Object)) { $QueryObject | Add-Member -MemberType NoteProperty -Name $Key -Value $this.Query[$Key] } $OAuthParametersString = [PsCustomObject]@{ HttpMethod = $this.HttpMethod BaseUri = $this.BaseUri Query = ($QueryObject | Format-List | Out-String).Trim() Uri = $this.UriBuilder.Uri.AbsoluteUri UnescapedQueryString = $this.UnescapedQueryString EscapedQueryString = $this.EscapedQueryString Body = $this.Body Form = $this.Form } return $OAuthParametersString | Format-List | Out-String } # ------------------------------------------------------------------------------------------------------------------ # hidden methods # note - only visible when using 'Get-Member -Force' # - the setter and getter methods for the class properties will be revealed with -Force # ------------------------------------------------------------------------------------------------------------------ # generate random base64 encoding 32 bytes hidden [string] GetNonce() { $NumberBytes = [Byte[]]::new(32) $Random = [System.Security.Cryptography.RandomNumberGenerator]::Create() $Random.GetBytes($NumberBytes) $Random.Dispose() return [System.Convert]::ToBase64String($NumberBytes) } # generate UNIX epoch time hidden [long] GetUnixTime(){ return [long]([datetime]::Now - [DateTime]::new(1970,1,1,0,0,0,[DateTimeKind]::Utc)).TotalSeconds } # create the base OAuth Headers hidden [hashtable] GetOAuthHeader($ApiKey,$AccessToken,$Nonce,$Timestamp) { return @{ oauth_consumer_key = $ApiKey oauth_nonce = $Nonce oauth_signature_method = 'HMAC-SHA1' oauth_timestamp = $Timestamp oauth_token = $AccessToken oauth_version = '1.0' } } hidden [string] GetParameterString($ApiKey,$AccessToken,$Nonce,$Timestamp) { # get OAuthHeader $OAuthHeader = $this.GetOAuthHeader($ApiKey,$AccessToken,$Nonce,$Timestamp) # collect parameters $Parameters = @{} foreach ($Key in $this.Query.Keys) { $Parameters.Add($Key,$this.Query[$Key]) } <#foreach ($Key in $this.Query.Keys) { if ([System.Uri]::UnescapeDataString($this.Query[$Key]) -eq $this.Query[$Key]) { $ParamValue = [System.Uri]::EscapeDataString($this.Query[$Key]) } else { $ParamValue = $this.Query[$Key] } $Parameters.Add( [System.Uri]::EscapeDataString($Key), $ParamValue #[System.Uri]::EscapeDataString($ParamValue) ) }#> # append OAuth header foreach ($Key in $OAuthHeader.Keys) { $Parameters.Add($Key,$OAuthHeader[$Key]) } # build parameter string, esnure that every key and value have been escaped only once $ParameterString = $Parameters.GetEnumerator() | Sort-Object -Property Name | ForEach-Object { $EscapedKey = ([System.Uri]::UnescapeDataString($_.Key) -eq $_.Key) ? [System.Uri]::EscapeDataString($_.Key) : $_.Key $EscapedValue = ([System.Uri]::UnescapeDataString($_.Value) -eq $_.Value) ? [System.Uri]::EscapeDataString($_.Value) : $_.Value '{0}={1}' -f $EscapedKey, $EscapedValue } return $ParameterString -join '&' } hidden [void] SetHttpMethod($HttpMethod) { if ($HttpMethod -match '\bGET\b|\bPOST\b|\bDELETE\b|\bPATCH\b|\bPUT\b|\bHEAD\b|\bOPTIONS\b|\bTRACE\b') { $this.HttpMethod = $HttpMethod.ToUpper() } else { throw "The value supplied for HttpMethod [$HttpMethod] is not a valid HTTP method." } } # maintain base uri separately as it will be used in GetOAuthSignatureString() method and in calling function hidden [void] SetBaseUri($BaseUri) { $this.BaseUri = $BaseUri $this.UriBuilder = $this.BaseUri } } #endregion #region source: \private\Get-SendMediaStatus.ps1 function Get-SendMediaStatus { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [Alias('media_id')] [string]$MediaId, [ValidateRange(1,[int]::MaxValue)] [int]$WaitSeconds ) $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://upload.twitter.com/1.1/media/upload.json', @{ 'command' = 'STATUS' 'media_id' = $MediaId } ) if ($PSBoundParameters.ContainsKey('WaitSeconds')) { $StatusCheck = 0 do { $StatusCheck++ $Activity = 'Waiting {0} seconds before refreshing upload status for media id {1}' -f $WaitSeconds, $MediaId $CurrentOperation = 'Check status #{0}' -f $StatusCheck $Status = 'Total seconds waited {0}' -f $TotalWaitSeconds Write-Progress -Activity $Activity -CurrentOperation $CurrentOperation -Status $Status Start-Sleep -Seconds $WaitSeconds $TotalWaitSeconds += $WaitSeconds $SendMediaStatus = Invoke-TwitterRequest -OAuthParameters $OAuthParameters if ($SendMediaStatus -is [ErrorRecord]) { $PSCmdlet.ThrowTerminatingError($SendMediaStatus) } if ($SendMediaStatus.'processing_info'.'error') { $SendMediaStatus.'processing_info'.'error' | Write-Error -ErrorAction Stop } if ($SendMediaStatus.'processing_info'.'check_after_secs') { $WaitSeconds = $SendMediaStatus.'processing_info'.'check_after_secs' -as [int] } } while ($SendMediaStatus.'processing_info'.'state' -eq 'in_progress') Write-Progress -Activity "Media upload status check completed" -Completed $SendMediaStatus } else { Invoke-TwitterRequest -OAuthParameters $OAuthParameters } } #endregion #region source: \private\Import-TwitterResource.ps1 function Import-TwitterResource { [CmdletBinding()] param( [Parameter(Mandatory,ParameterSetName='Configuration')] [switch]$Configuration, [Parameter(Mandatory,ParameterSetName='Languages')] [switch]$Languages, [Parameter(Mandatory,ParameterSetName='ErrorMapping')] [switch]$ErrorMapping ) switch ($PSCmdlet.ParameterSetName) { 'Configuration' { $ResourcePath = $ConfigurationSavePath } 'Languages' { $ResourcePath = $LanguagesSavePath } 'ErrorMapping' { $ResourcePath = $ErrorMappingPath } } 'Importing {0} from {1}' -f $PSCmdlet.ParameterSetName,$ResourcePath | Write-Verbose try { if (Test-Path -Path $ResourcePath) { $Resource = Get-Content -Path $ResourcePath -Raw | ConvertFrom-Json } else { if ($PSCmdlet.ParameterSetName -ne 'ErrorMapping') { $ErrorMessage = '{0}Unable to find {1} resource data.{0}Please export the resource using "Export-TwitterResource -{1}"' -f [System.Environment]::NewLine,$PSCmdlet.ParameterSetName $ErrorMessage | Write-Warning return } } } catch { $PSCmdlet.ThrowTerminatingError($_) } switch ($PSCmdlet.ParameterSetName) { 'Configuration' { [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $script:TwitterConfiguration = $Resource } 'Languages' { [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $script:TwitterLanguages = $Resource } 'ErrorMapping' { [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $script:TwitterErrorMapping = $Resource } } } #endregion #region source: \private\Invoke-TwitterCursorRequest.ps1 function Invoke-TwitterCursorRequest { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipeline)] [OAuthParameters]$OAuthParameters, [string]$ReturnValue ) $NextCursor = -1 do { if ($OAuthParameters.BaseUri -eq 'https://api.twitter.com/1.1/direct_messages/events/list.json' -and $NextCursor -eq -1) { # Get-TwitterDM # do not send a cursor for the first API call } else { if ($OAuthParameters.Query.Keys -match 'cursor') { $OAuthParameters.Query.Remove('cursor') } $OAuthParameters.Query.Add('cursor',$NextCursor) $OAuthParameters.SetQuery($OAuthParameters.Query) } try { $TwitterRequest = Invoke-TwitterRequest -OAuthParameters $OAuthParameters if ($TwitterRequest -is [System.Management.Automation.ErrorRecord]) { $PSCmdlet.ThrowTerminatingError($TwitterRequest) } if ($TwitterRequest.psobject.Properties.Name -contains $ReturnValue) { $TwitterRequest.$ReturnValue } else { $TwitterRequest } $NextCursor = $TwitterRequest.next_cursor # Get-TwitterDM if ($null -eq $NextCursor) { break } } catch { $PSCmdlet.WriteError($_) break } } while ($NextCursor -ne 0) } #endregion #region source: \private\Invoke-TwitterPageRequest.ps1 function Invoke-TwitterPageRequest { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipeline)] [OAuthParameters]$OAuthParameters, [Parameter(Mandatory)] [int]$Pages ) 'Incoming OAuthParameters:',$OAuthParameters | Write-Verbose $CurrentCount = 0 $AllPages = @() for ($CurrentPage = 1 ; $CurrentPage -le $Pages; $CurrentPage++) { if ($OAuthParameters.Query.Keys -match 'page') { $OAuthParameters.Query.Remove('page') } $OAuthParameters.Query.Add('page',$CurrentPage) if ($CurrentPage -eq $Pages) { $Count = $MaxResults - $CurrentCount } else { $Count = 20 } if ($OAuthParameters.Query.Keys -match 'count') { $OAuthParameters.Query.Remove('count') } $OAuthParameters.Query.Add('count',$Count) $OAuthParameters.SetQuery($OAuthParameters.Query) 'OAuthParameters:',$OAuthParameters | Write-Verbose try { $PageRequest = Invoke-TwitterRequest -OAuthParameters $OAuthParameters $AllPages = $AllPages + $PageRequest $CurrentCount = $CurrentCount + $Count } catch { $PSCmdlet.WriteError($_) break } } $AllPages } #endregion #region source: \private\Invoke-TwitterRequest.ps1 function Invoke-TwitterRequest { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipeline)] [OAuthParameters]$OAuthParameters, [switch]$SkipHistory ) try { $OAuthHeaderString = $OAuthParameters.GetOAuthSignatureString( $OAuth['ApiKey'],$OAuth['ApiSecret'], $OAuth['AccessToken'],$OAuth['AccessTokenSecret'], $null,$null ) '{0}OAuthHeaderString:{0}{1}' -f [System.Environment]::NewLine,$OAuthHeaderString | Write-Verbose $WebRequestParams = @{ Uri = $OAuthParameters.UriBuilder.Uri.AbsoluteUri Method = $OAuthParameters.HttpMethod Headers = @{ 'Authorization' = $OAuthHeaderString } ContentType = 'application/json' ResponseHeadersVariable = 'TwitterResponse' StatusCodeVariable = 'TwitterStatusCode' Verbose = $false } if ($OAuthParameters.Form) { $WebRequestParams.Add('Form',$OAuthParameters.Form) } elseif ($OAuthParameters.Body) { $WebRequestParams.Add('Body',$OAuthParameters.Body) } '{0}WebRequest Parameters:{0}{1}{0}' -f [System.Environment]::NewLine,($WebRequestParams.GetEnumerator() | Out-String).Trim() | Write-Verbose Invoke-RestMethod @WebRequestParams $ResponseData = [PsCustomObject]@{ TwitterResponse = $TwitterResponse StatusCode = $TwitterStatusCode Uri = $OAuthParameters.BaseUri QueryString = $OAuthParameters.UriBuilder.Uri.Query HttpMethod = $OAuthParameters.HttpMethod } if ($PSBoundParameters.ContainsKey('SkipHistory')) { $ResponseData | Write-TwitterResponseData -SkipHistory } else { $ResponseData | Write-TwitterResponseData } } catch [Microsoft.PowerShell.Commands.HttpResponseException] { $_ | New-TwitterErrorRecord } catch { $PSCmdlet.ThrowTerminatingError($_) } } #endregion #region source: \private\New-TwitterErrorRecord.ps1 function New-TwitterErrorRecord { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipeline)] [ErrorRecord]$ErrorRecord ) try { $Command = (Get-PSCallStack).Where{$_.Command -notmatch 'ErrorRecord|ResponseData|Request|ScriptBlock'}.Command $Response = $ErrorRecord.Exception.Response if ($Response.Headers.ToString() -match '(?:x-response-time:\s)(\d+)') { $ResponseTime = $Matches[1] } if ($Response.RequestMessage.RequestUri.Query.Length -gt 0) { $Uri = $Response.RequestMessage.RequestUri.AbsoluteUri.Replace($Response.RequestMessage.RequestUri.Query,'') } else { $Uri = $Response.RequestMessage.RequestUri.AbsoluteUri } $Status = '{0} {1}' -f $Response.StatusCode.Value__,$Response.ReasonPhrase $Server = $Response.Headers.Where{$_.Key -eq 'server'}.Value try { if ($ErrorRecord.ErrorDetails) { $TwitterError = ($ErrorRecord.ErrorDetails.Message | ConvertFrom-Json).errors $ErrorCategory = $TwitterErrorMapping.Where{$_.ErrorCode -eq $TwitterError.code}.Exception $ErrorMessage = $TwitterError.message $ErrorCode = $TwitterError.code } else { $MappedError = $TwitterErrorMapping.Where{$_.HttpStatusCode -eq $Response.StatusCode.Value__} $ErrorCategory = $MappedError.Exception if ($MappedError.Message) { $ErrorMessage = $MappedError.Message } else { $ErrorMessage = $TwitterError.ReasonPhrase } $ErrorCode = $null } } catch { $ErrorCategory = $ErrorRecord.CategoryInfo.Category $ErrorMessage = $Response.StatusCode $ErrorCode = $null } $ResponseData = [PsCustomObject]@{ PSTypeName = 'Twitter.Error' Command = $Command HttpMethod = $Response.RequestMessage.Method.ToString() Uri = $Uri QueryString = $Response.RequestMessage.RequestUri.Query Status = $Status Message = $ErrorMessage Server = $Server ResponseTime = $ResponseTime Response = $Response ErrorCode = $ErrorCode } $TwitterHistoryList.Add($ResponseData) Write-Information -MessageData $ResponseData $TwitterException = [HttpResponseException]::new($ErrorMessage,$Response) [ErrorRecord]::new($TwitterException,$Command,$ErrorCategory,$ResponseData.Uri) } catch { $PSCmdlet.ThrowTerminatingError($_) } } #endregion #region source: \private\New-TwitterQuery.ps1 function New-TwitterQuery { [CmdletBinding()] param( [hashtable]$ApiParameters ) $TwitterQuery = [hashtable]::new() foreach ($Parameter in $ApiParameters.Keys) { switch ($Parameter) { 'ScreenName' { # GET friendships/lookup allows for multiple input if ($ApiParameters[$Parameter] -is [array]) { $TwitterQuery.Add('screen_name',($ApiParameters[$Parameter] -join ',')) } else { $TwitterQuery.Add('screen_name',$ApiParameters[$Parameter]) } } 'UserId' { # GET friendships/lookup allows for multiple input if ($ApiParameters[$Parameter] -is [array]) { $TwitterQuery.Add('user_id',($ApiParameters[$Parameter] -join ',')) } else { $TwitterQuery.Add('user_id',$ApiParameters[$Parameter]) } } 'Count' { $TwitterQuery.Add('count',$ApiParameters[$Parameter]) } 'TweetId' { $TwitterQuery.Add('id',$ApiParameters[$Parameter]) } 'SinceId' { $TwitterQuery.Add('since_id',$ApiParameters[$Parameter]) } 'MaxId' { $TwitterQuery.Add('max_id',$ApiParameters[$Parameter]) } 'SkipStatus' { $TwitterQuery.Add('skip_status',$ApiParameters[$Parameter]) } 'ResultsPerPage' { $TwitterQuery.Add('count',$ApiParameters[$Parameter]) } 'SourceScreenName' { $TwitterQuery.Add('source_screen_name',$ApiParameters[$Parameter]) } 'SourceUserId' { $TwitterQuery.Add('source_id',$ApiParameters[$Parameter]) } 'TargetScreenName' { $TwitterQuery.Add('target_screen_name',$ApiParameters[$Parameter]) } 'TargetUserId' { $TwitterQuery.Add('target_id',$ApiParameters[$Parameter]) } 'Slug' { $TwitterQuery.Add('slug',$ApiParameters[$Parameter]) } 'OwnerScreenName' { $TwitterQuery.Add('owner_screen_name',$ApiParameters[$Parameter]) } 'OwnerId' { $TwitterQuery.Add('owner_id',$ApiParameters[$Parameter]) } 'ListId' { $TwitterQuery.Add('list_id',$ApiParameters[$Parameter]) } 'OwnedListFirst' { $TwitterQuery.Add('reverse','true') } 'ExcludeRetweets' { $TwitterQuery.Add('include_rts','false') } 'SearchString' { $TwitterQuery.Add('q',[System.Uri]::EscapeDataString($ApiParameters[$Parameter])) } 'MaxResults' { $TwitterQuery.Add('count',$ApiParameters[$Parameter]) } 'TweetMode' { $TweetModeValue = $ApiParameters[$Parameter] -eq 'Compatibility' ? 'compat' : 'extended' $TwitterQuery.Add('tweet_mode',$TweetModeValue) } } } if ($ApiParameters.ContainsKey('ExcludeEntities')) { $TwitterQuery.Add('include_entities','false') } else { $TwitterQuery.Add('include_entities','true') } '{0}Twitter Query:{0}{1}' -f [System.Environment]::NewLine,($TwitterQuery.GetEnumerator() | Out-String).Trim() | Write-Verbose $TwitterQuery } #endregion #region source: \private\New-ValidationErrorRecord.ps1 function New-ValidationErrorRecord { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Message, [Parameter(Mandatory)] [string]$Target, [Parameter(Mandatory)] [string]$ErrorId ) [System.Management.Automation.ErrorRecord]::new( [ValidationMetadataException]::new($Message), $ErrorId, 'InvalidArgument', $Target ) } #endregion #region source: \private\Set-TwitterMediaAltImageText.ps1 function Set-TwitterMediaAltImageText { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [Alias('media_id')] [string]$MediaId, [ValidateLength(1,1000)] [string]$AltImageText ) $OAuthParameters = [OAuthParameters]::new( 'POST', 'https://upload.twitter.com/1.1/media/metadata/create.json' ) $OAuthParameters.Body = ('{{"media_id":"{0}","alt_text":{{"text":"{1}"}}}}' -f $MediaId,$AltImageText) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \private\Write-TwitterResponseData.ps1 function Write-TwitterResponseData { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName)] [object]$TwitterResponse, [Parameter(ValueFromPipelineByPropertyName)] [int32]$StatusCode, [Parameter(ValueFromPipelineByPropertyName)] [string]$Uri, [Parameter(ValueFromPipelineByPropertyName)] [string]$HttpMethod, [Parameter(ValueFromPipelineByPropertyName)] [string]$QueryString, [switch]$SkipHistory ) try { $Command = (Get-PSCallStack).Where{$_.Command -notmatch 'TwitterResource|ErrorRecord|ResponseData|Request|ScriptBlock'}.Command if ($Command -is [array]) { $Command = $Command[0] } if ($TwitterResponse.Status) { $Status = $TwitterResponse.Status[0] } if ($TwitterResponse.Server) { $Server = $TwitterResponse.Server[0] } if ($TwitterResponse['x-response-time']) { $ResponseTime = $TwitterResponse['x-response-time'][0] } $ResponseData = [PsCustomObject]@{ PSTypeName = 'Twitter.Response' Command = $Command HttpMethod = $HttpMethod Uri = $Uri QueryString = $QueryString Status = $Status Server = $Server ResponseTime = $ResponseTime RateLimit = $null RateLimitRemaining = $null RateLimitReset = $null Response = $TwitterResponse } if ($TwitterResponse['x-rate-limit-limit']) { [int]$ResponseData.RateLimit = $TwitterResponse['x-rate-limit-limit'][0] } if ($TwitterResponse['x-rate-limit-remaining']) { [int]$ResponseData.RateLimitRemaining = $TwitterResponse['x-rate-limit-remaining'][0] } if ($TwitterResponse['x-rate-limit-reset']) { [long]$Ticks = $TwitterResponse['x-rate-limit-reset'][0] $ResponseData.RateLimitReset = (Get-Date -Date '1/1/1970').AddSeconds($Ticks).ToLocalTime() } if ($ResponseData.RateLimitRemaining -eq 0 -and $null -ne $ResponseData.RateLimitRemaining) { $RateLimitReached = 'Rate limit of {0} has been reached. Please wait until {1} before making another attempt for this resource.' -f $ResponseData.RateLimit,$ResponseData.RateLimitReset $RateLimitReached | Write-Error -ErrorAction Stop } if (($ResponseData.RateLimitRemaining -le 5 -and $null -ne $ResponseData.RateLimitRemaining) -or $RateLimitWarning) { $RateWarningMessage = 'The rate limit for this resource is {0}. There are {1} remaining calls to this resource until {2}. ' -f $ResponseData.RateLimit, $ResponseData.RateLimitRemaining, $ResponseData.RateLimitReset $RateWarningMessage | Write-Warning } if (-Not $PSBoundParameters.ContainsKey('SkipHistory')) { $TwitterHistoryList.Add($ResponseData) } Write-Information -MessageData $ResponseData } catch { $PSCmdlet.ThrowTerminatingError($_) } } #endregion #region source: \public\api\authentication\Export-TwitterAuthentication.ps1 function Export-TwitterAuthentication { [CmdletBinding()] param() try { if (-Not (Test-Path -Path $OAuthTokenSavePath)) { $Action = 'new' New-Item -Path $OAuthTokenSavePath -Force -ItemType File | Out-Null } else { $Action = 'existing' } $OAuth | ConvertTo-Json | ConvertTo-SecureString -AsPlainText | ConvertFrom-SecureString | Set-Content -Path $OAuthTokenSavePath -Force 'Saved Twitter credentials to {0} file: {1}' -f $Action,$OAuthTokenSavePath | Write-Verbose } catch { $PSCmdlet.ThrowTerminatingError($_) } } #endregion #region source: \public\api\authentication\Import-TwitterAuthentication.ps1 function Import-TwitterAuthentication { [CmdletBinding()] param() 'Checking Twitter credentials file.' | Write-Verbose $SetValues = 'Please use Set-TwitterAuthentication to set the requried API keys and secrets. Use the -Persist switch to save the values to disk.' if (Test-Path -Path $OAuthTokenSavePath) { 'Twitter credentials file found.' | Write-Verbose try { 'Attempting to import Twitter credentials file.' | Write-Verbose # read the encrypted credentials file, decrypt, and convert from JSON to object $OAuthFromDisk = Get-Content -Path $OAuthTokenSavePath | ConvertTo-SecureString -ErrorAction Stop | ConvertFrom-SecureString -AsPlainText | ConvertFrom-Json # ensure that the credentials file has the correct keys/attributes foreach ($OAuthKey in 'ApiKey','ApiSecret','AccessToken','AccessTokenSecret','BearerToken') { if ($OAuthFromDisk.psobject.Properties.Name -notcontains $OAuthKey) { Write-Error -ErrorAction Stop } } # ensure that we have values for the four required keys if ($OAuthFromDisk.psobject.Properties.Where{$_.Name -ne 'BearerToken' -and $null -ne $_.Value}.count -eq 4) { $OAuth['ApiKey'] = $OAuthFromDisk.ApiKey $OAuth['ApiSecret'] = $OAuthFromDisk.ApiSecret $OAuth['AccessToken'] = $OAuthFromDisk.AccessToken $OAuth['AccessTokenSecret'] = $OAuthFromDisk.AccessTokenSecret $OAuth['BearerToken'] = $OAuthFromDisk.BearerToken 'Twitter credentials file imported.' | Write-Verbose } else { 'Authentication file missing one or more values. {0}' -f $SetValues | Write-Warning } } catch { 'Authentication file appears to be corrupted. {0}' -f $SetValues | Write-Warning } } else { $SetValues | Write-Warning } } #endregion #region source: \public\api\authentication\Set-TwitterAuthentication.ps1 function Set-TwitterAuthentication { [CmdletBinding()] param ( [SecureString]$ApiKey = (Read-Host -Prompt 'API Key' -AsSecureString), [SecureString]$ApiSecret = (Read-Host -Prompt 'API Secret' -AsSecureString), [SecureString]$AccessToken = (Read-Host -Prompt 'Access Token' -AsSecureString), [SecureString]$AccessTokenSecret = (Read-Host -Prompt 'Access Token Secret' -AsSecureString), [switch]$Persist ) try { $OAuth['ApiKey'] = $ApiKey | ConvertFrom-SecureString -AsPlainText $OAuth['ApiSecret'] = $ApiSecret | ConvertFrom-SecureString -AsPlainText $OAuth['AccessToken'] = $AccessToken | ConvertFrom-SecureString -AsPlainText $OAuth['AccessTokenSecret'] = $AccessTokenSecret | ConvertFrom-SecureString -AsPlainText if (Test-TwitterAuthentication) { 'Successfully connected to Twitter.' | Write-Verbose if ($PSBoundParameters.ContainsKey('Persist')) { Export-TwitterAuthentication } } else { 'Failed authentication verification. Please check your credentials and try again.' | Write-Error -ErrorAction Stop } } catch { $PSCmdlet.ThrowTerminatingError($_) } } #endregion #region source: \public\api\authentication\Set-TwitterBearerToken.ps1 function Set-TwitterBearerToken { [CmdletBinding()] param( [switch]$Persist ) try { $Credential = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(('{0}:{1}' -f $OAuth['ApiKey'],$OAuth['ApiSecret']))) $BasicAuth = 'Basic {0}' -f $Credential $WebRequestParams = @{ Uri = 'https://api.twitter.com/oauth2/token' Method = 'POST' Headers = @{ 'Authorization' = $BasicAuth } ContentType = 'application/x-www-form-urlencoded' ResponseHeadersVariable = 'TwitterResponse' StatusCodeVariable = 'TwitterStatusCode' Body = 'grant_type=client_credentials' Verbose = $false } $Token = Invoke-RestMethod @WebRequestParams $OAuth['BearerToken'] = $Token.access_token if ($PSBoundParameters.ContainsKey('Persist')) { Export-TwitterAuthentication } $ResponseData = [PsCustomObject]@{ TwitterResponse = $TwitterResponse StatusCode = $TwitterStatusCode Uri = $WebRequestParams.Uri QueryString = $null HttpMethod = $WebRequestParams.Method } $ResponseData | Write-TwitterResponseData } catch [Microsoft.PowerShell.Commands.HttpResponseException] { $_ | New-TwitterErrorRecord } catch { $PSCmdlet.ThrowTerminatingError($_) } } #endregion #region source: \public\api\authentication\Test-TwitterAuthentication.ps1 function Test-TwitterAuthentication { [CmdletBinding()] param() $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/account/verify_credentials.json', @{ include_entities = 'false' skip_status = 'true' } ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters -InformationVariable Response | Out-Null $Response.MessageData.Status -match '200' ? $true : $false } #endregion #region source: \public\api\direct_message\Get-TwitterDM.ps1 function Get-TwitterDM { [CmdletBinding()] param( [ValidateNotNullOrEmpty()] [Alias('Id')] [string]$DirectMessageId, [ValidateRange(1,50)] [int]$Count = 20 ) if ($PSBoundParameters.ContainsKey('DirectMessageId')) { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/direct_messages/events/show.json', @{'id' = $DirectMessageId; 'count' = $Count} ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } else { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/direct_messages/events/list.json', @{'count'= $Count } ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue events } } #endregion #region source: \public\api\direct_message\Publish-TwitterDM.ps1 function Publish-TwitterDM { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [Alias('id')] [string]$UserId, [Parameter(Mandatory)] [ValidateLength(1,10000)] [string]$Message, [Parameter(ValueFromPipelineByPropertyName)] [Alias('media_id')] [string]$MediaId ) $MessageTemplate = '{{"event":{{"type":"message_create","message_create":{{"target":{{"recipient_id":"{0}"}},"message_data":{{"text":"{1}"}}}}}}}}' $MessageWithMediaTemplate = '{{"event":{{"type":"message_create","message_create":{{"target":{{"recipient_id":"{0}"}},"message_data":{{"text":"{1}","attachment":{{"type":"media","media":{{"id":{2}}}}}}}}}}}}}' $OAuthParameters = [OAuthParameters]::new( 'POST', 'https://api.twitter.com/1.1/direct_messages/events/new.json' ) if ($PSBoundParameters.ContainsKey('MediaId')) { $Body = $MessageWithMediaTemplate -f $UserId,$Message,$MediaId } else { $Body = $MessageTemplate -f $UserId,$Message } $OAuthParameters.Body = $Body.Replace("`r`n",'\n') Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\direct_message\Unpublish-TwitterDM.ps1 function Unpublish-TwitterDM { [CmdLetBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [Alias('Id')] [string]$DirectMessageId ) $OAuthParameters = [OAuthParameters]::new( 'DELETE', 'https://api.twitter.com/1.1/direct_messages/events/destroy.json', @{'id'= $DirectMessageId } ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\lists\Get-TwitterList.ps1 function Get-TwitterList { [CmdletBinding(DefaultParameterSetName='ListScreenName')] param( [Parameter(ParameterSetName='ListScreenName')] [string]$ScreenName, [Parameter(Mandatory,ParameterSetName='ListUserId')] [ValidateNotNullOrEmpty()] [string]$UserId, [Parameter(ParameterSetName='ListScreenName')] [Parameter(ParameterSetName='ListUserId')] [switch]$OwnedListFirst, [Parameter(Mandatory,ParameterSetName='ShowId')] [ValidateNotNullOrEmpty()] [string]$ListId, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerScreenName')] [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerId')] [ValidateNotNullOrEmpty()] [string]$Slug, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerScreenName')] [ValidateNotNullOrEmpty()] [string]$OwnerScreenName, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerId')] [ValidateNotNullOrEmpty()] [long]$OwnerId ) if ($PSCmdlet.ParameterSetName -match 'List') { $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/lists/list.json', $Query ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } else { $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'Get', 'https://api.twitter.com/1.1/lists/show.json', $Query ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } } #endregion #region source: \public\api\lists\Get-TwitterListByOwner.ps1 function Get-TwitterListByOwner { [CmdletBinding(DefaultParameterSetName='ScreenName')] param( [Parameter(Mandatory,ParameterSetName='ScreenName')] [ValidateNotNullOrEmpty()] [string]$ScreenName, [Parameter(Mandatory,ParameterSetName='UserId')] [ValidateNotNullOrEmpty()] [long]$UserId, [ValidateRange(1,1000)] [Alias('Count')] [int]$ResultsPerPage=20 ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/lists/ownerships.json', $Query ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue lists } #endregion #region source: \public\api\lists\Get-TwitterListMember.ps1 function Get-TwitterListMember { [CmdletBinding(DefaultParameterSetName='ListId')] param( [Parameter(Mandatory,ParameterSetName='ListId')] [string]$ListId, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerScreenName')] [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerId')] [ValidateNotNullOrEmpty()] [string]$Slug, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerScreenName')] [ValidateNotNullOrEmpty()] [string]$OwnerScreenName, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerId')] [ValidateNotNullOrEmpty()] [long]$OwnerId, [ValidateRange(1,5000)] [int]$ResultsPerPage = 20, [switch]$SkipStatus, [switch]$ExcludeEntities ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/lists/members.json', $Query ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue users } #endregion #region source: \public\api\lists\Get-TwitterListSubscriber.ps1 function Get-TwitterListSubscriber { [CmdletBinding()] param( [Parameter(Mandatory,ParameterSetName='ShowId')] [ValidateNotNullOrEmpty()] [string]$ListId, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerScreenName')] [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerId')] [ValidateNotNullOrEmpty()] [string]$Slug, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerScreenName')] [ValidateNotNullOrEmpty()] [string]$OwnerScreenName, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerId')] [ValidateNotNullOrEmpty()] [long]$OwnerId, [ValidateRange(1,5000)] [int]$ResultsPerPage = 20, [switch]$SkipStatus, [switch]$ExcludeEntities ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/lists/subscribers.json', $Query ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue users } #endregion #region source: \public\api\lists\Get-TwitterListSubscription.ps1 function Get-TwitterListSubscription { [CmdletBinding(DefaultParameterSetName='ScreenName')] param( [Parameter(Mandatory,ParameterSetName='ScreenName')] [ValidateNotNullOrEmpty()] [string]$ScreenName, [Parameter(Mandatory,ParameterSetName='UserId')] [ValidateNotNullOrEmpty()] [string]$UserId, [ValidateRange(1,1000)] [int]$ResultsPerPage = 20 ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/lists/subscriptions.json', $Query ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue lists } #endregion #region source: \public\api\lists\Get-TwitterListTweets.ps1 function Get-TwitterListTweets { [Alias('Get-TwitterListStatus')] [CmdletBinding(DefaultParameterSetName='ListId')] param( [Parameter(Mandatory,ParameterSetName='ListId')] [string]$ListId, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerScreenName')] [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerId')] [ValidateNotNullOrEmpty()] [string]$Slug, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerScreenName')] [ValidateNotNullOrEmpty()] [string]$OwnerScreenName, [Parameter(Mandatory,ParameterSetName='ShowSlugOwnerId')] [ValidateNotNullOrEmpty()] [long]$OwnerId, [long]$SinceId, [long]$MaxId, [ValidateRange(1,200)] [long]$Count = 20, [switch]$ExcludeEntities, [switch]$ExcludeRetweets ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/lists/statuses.json', $Query ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters | ConvertTo-Json -Depth 10 | ConvertFrom-Json } #endregion #region source: \public\api\media\Send-TwitterMedia.ps1 function Send-TwitterMedia { [CmdletBinding()] [Alias('Send-Media')] param( [Parameter(Mandatory,ValueFromPipeline)] [ValidateScript({Resolve-Path -Path $_})] [string]$Path, [Parameter(Mandatory)] [ValidateSet('TweetImage','TweetVideo','TweetGif','DMImage','DMVideo','DMGif')] [string]$Category, [ValidateLength(1,1000)] [string]$AltImageText, [ValidateCount(1,100)] [int[]]$AddOwners ) begin { $MediaFileInfo = Get-ChildItem $Path # get mime type by extension, see https://github.com/SCRT-HQ/PSGSuite/blob/master/PSGSuite/Private/Get-MimeType.ps1 for inspiration # there's nothing currently in .Net Core that could derive the type from the content $MediaMimeTypes = @{ gif = 'image/gif' jpg = 'image/jpeg' jpeg = 'image/jpeg' png = 'image/png' webp = 'image/webp' mp4 = 'video/mp4' mov = 'video/quicktime' } $MimeType = $MediaMimeTypes[$MediaFileInfo.Extension.TrimStart('.')] # validate size of file # validate if detected mimetype matches category $SizeLimitExceededMessage = 'The size of media {0} exceeded the limit of {2} bytes. Please try again.' $CategoryMimeTypeMismatch = 'Category {0} does not match the media mimetype of {1}. Please try again.' $CategoryAltImgText = 'Category {0} does not allow the AltImageText. Please try again.' $ValidationErrorRecord = @{ Message = [String]::Empty Target = $MediaFileInfo.Name ErrorId = $null } switch -regex ($Category) { 'Image' { if ($MediaFileInfo.Length -gt 5MB) { $ValidationErrorRecord.Message = $SizeLimitExceededMessage -f $Category,$MediaFileInfo.Name,5MB $ValidationErrorRecord.ErrorId = 'SizeLimitExceeded' $PSCmdlet.ThrowTerminatingError((New-ValidationErrorRecord @ValidationErrorRecord)) } if ($MimeType -notmatch 'image') { $ValidationErrorRecord.Message = $CategoryMimeTypeMismatch -f $Category,$MimeType $ValidationErrorRecord.ErrorId = 'MediaCategoryMimeTypeMismatch' $PSCmdlet.ThrowTerminatingError((New-ValidationErrorRecord @ValidationErrorRecord)) } break } 'Video' { if ($MediaFileInfo.Length -gt 512MB) { $ValidationErrorRecord.Message = $SizeLimitExceededMessage -f $Category,$MediaFileInfo.Name,512MB $ValidationErrorRecord.ErrorId = 'SizeLimitExceeded' $PSCmdlet.ThrowTerminatingError((New-ValidationErrorRecord @ValidationErrorRecord)) } if ($MimeType -notmatch 'video') { $ValidationErrorRecord.Message = $CategoryMimeTypeMismatch -f $Category,$MimeType $ValidationErrorRecord.ErrorId = 'MediaCategoryMimeTypeMismatch' $PSCmdlet.ThrowTerminatingError((New-ValidationErrorRecord @ValidationErrorRecord)) } break } 'Gif' { if ($MediaFileInfo.Length -gt 15MB) { $ValidationErrorRecord.Message = $SizeLimitExceededMessage -f $Category,$MediaFileInfo.Name,15MB $ValidationErrorRecord.ErrorId = 'SizeLimitExceeded' $PSCmdlet.ThrowTerminatingError((New-ValidationErrorRecord @ValidationErrorRecord)) } if ($MimeType -ne 'image/gif') { $ValidationErrorRecord.Message = $CategoryMimeTypeMismatch -f $Category,$MimeType $ValidationErrorRecord.ErrorId = 'MediaCategoryMimeTypeMismatch' $PSCmdlet.ThrowTerminatingError((New-ValidationErrorRecord @ValidationErrorRecord)) } break } } if ($PSBoundParameters.ContainsKey('AltImageText') -and $MimeType -match 'video') { $ValidationErrorRecord.Message = $CategoryAltImgText -f $Category,$MimeType $ValidationErrorRecord.ErrorId = 'MediaCategoryNoSupportForAltImgText' $PSCmdlet.ThrowTerminatingError((New-ValidationErrorRecord @ValidationErrorRecord)) } $MediaCategory = switch ($Category) { 'TweetImage' { 'tweet_image' } 'TweetVideo' { 'tweet_video' } 'TweetGif' { 'tweet_gif' } 'DMImage' { 'dm_image' } 'DMVideo' { 'dm_video' } 'DMGif' { 'dm_gif' } } $MediaUploadUrl = 'https://upload.twitter.com/1.1/media/upload.json' $TotalBytes = $MediaFileInfo.Length } process { 'Reading file {0}' -f $MediaFileInfo.FullName | Write-Verbose # read the image into memory $BufferSize = 900000 $Buffer = [Byte[]]::new($BufferSize) $Reader = [System.IO.File]::OpenRead($MediaFileInfo.FullName) $Media = [ArrayList]::new() do { $BytesRead = $Reader.Read($Buffer, 0 , $BufferSize) $null = $Media.Add([Convert]::ToBase64String($Buffer, 0, $BytesRead)) } while ($BytesRead -eq $BufferSize) $Reader.Dispose() # ------------------------------------------------------------------------------------------ # INIT phase 'Beginning INIT phase - media size {0}, category {1}, type {2}' -f $TotalBytes,$MediaCategory,$MimeType | Write-Verbose $OAuthParameters = [OAuthParameters]::new('POST',$MediaUploadUrl) $OAuthParameters.Form = @{ command = 'INIT' total_bytes = $TotalBytes media_category = $MediaCategory media_type = $MimeType } if ($PSBoundParameters.ContainsKey('AddOwners')) { $OAuthParameters.Form.Add(($AddOwners -join ',')) } $SendMediaInitResult = Invoke-TwitterRequest -OAuthParameters $OAuthParameters -Verbose:$false if ($SendMediaInitResult-is [ErrorRecord]) { $PSCmdlet.ThrowTerminatingError($SendMediaInitResult) } $MediaId = $SendMediaInitResult.'media_id' 'Upload for media id {0} successfully initiated' -f $MediaId | Write-Verbose # ------------------------------------------------------------------------------------------ # APPEND phase 'Beginning APPEND phase' | Write-Verbose $Index = 0 foreach ($Chunk in $Media) { $PercentComplete = (($Index + 1) / $Media.Count) * 100 $Activity = "Uploading media file '{0}' with id {1}" -f $MediaFileInfo.Name,$MediaId $CurrentOperation = "Media chunk #{0}" -f $Index $Status = "{0}% Complete:" -f $PercentComplete Write-Progress -Activity $Activity -CurrentOperation $CurrentOperation -Status $Status -PercentComplete $PercentComplete $OAuthParameters = [OAuthParameters]::new('POST',$MediaUploadUrl) $OAuthParameters.Form = @{ command = 'APPEND' media_id = $MediaId media_data = $Media[$Index] segment_index = $Index } $SendMediaAppendResultParam = @{ OAuthParameters = $OAuthParameters Verbose = $false } if ($Index -eq 0) { $SendMediaAppendResultParam.Add('SkipHistory',$true) } $SendMediaAppendResult = Invoke-TwitterRequest @SendMediaAppendResultParam if ($SendMediaAppendResult -is [ErrorRecord]) { $PSCmdlet.ThrowTerminatingError($SendMediaAppendResult) } $Index++ } Write-Progress -Activity 'Media upload append phase completed' -Completed # ------------------------------------------------------------------------------------------ # FINALIZE phase 'Beginning FINALIZE phase' | Write-Verbose $OAuthParameters = [OAuthParameters]::new('POST',$MediaUploadUrl) $OAuthParameters.Form = @{ command = 'FINALIZE' media_id = $MediaId } $SendMediaFinalizeResult = Invoke-TwitterRequest -OAuthParameters $OAuthParameters -Verbose:$false if ($SendMediaFinalizeResult -is [ErrorRecord]) { $PSCmdlet.ThrowTerminatingError($SendMediaFinalizeResult) } # ------------------------------------------------------------------------------------------ # STATUS phase if ($SendMediaFinalizeResult.'processing_info'.'check_after_secs') { 'Beginning STATUS phase' | Write-Verbose $WaitSeconds = $SendMediaFinalizeResult.'processing_info'.'check_after_secs' -as [int] $SendMediaStatus = Get-SendMediaStatus -MediaId $MediaId -WaitSeconds $WaitSeconds -Verbose:$false $SendMediaCompletionResults = $SendMediaStatus } else { $SendMediaCompletionResults = $SendMediaFinalizeResult } # ------------------------------------------------------------------------------------------ # Add AltImageText phase if ($AltImageText.Length -gt 0) { 'Adding AltImageText to media {0}' -f $MediaId | Write-Verbose Set-TwitterMediaAltImageText -MediaId $MediaId -AltImageText $AltImageText -Verbose:$false | Out-Null $LastTwitterCommand = Get-TwitterHistory -Last 1 if ($LastTwitterCommand.Status -match '200') { 'Alt image text successfully added to media' | Write-Verbose } } 'Media upload complete' | Write-Verbose $SendMediaCompletionResults } end { } } #endregion #region source: \public\api\searches\Add-TwitterSavedSearch.ps1 function Add-TwitterSavedSearch { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$SearchString ) $OAuthParameters = [OAuthParameters]::new( 'POST', 'https://api.twitter.com/1.1/saved_searches/create.json', @{ query = [System.Uri]::EscapeDataString($SearchString) } ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\searches\Get-TwitterSavedSearch.ps1 function Get-TwitterSavedSearch { [CmdletBinding()] param( [ValidateNotNullOrEmpty()] [Alias('Id')] [long]$SearchId ) if ($PSBoundParameters.ContainsKey('SearchId')) { $Url = 'https://api.twitter.com/1.1/saved_searches/show/{0}.json' -f $SearchId } else { $Url = 'https://api.twitter.com/1.1/saved_searches/list.json' } $OAuthParameters = [OAuthParameters]::new('GET',$Url) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\searches\Remove-TwitterSavedSearch.ps1 function Remove-TwitterSavedSearch { [CmdletBinding(SupportsShouldProcess,ConfirmImpact='high')] param( [Parameter(Mandatory)] [Alias('Id')] [ValidateNotNullOrEmpty()] [long[]]$SearchId ) begin { $SavedSearches = Get-TwitterSavedSearch } process { foreach ($Id in $SearchId) { $ThisSearch = $SavedSearches.Where({$_.id -eq $Id}) $ThisSearchInfo = 'Search : {0}, Created: {1}' -f $ThisSearch.query,$ThisSearch.created_at if ($ThisSearch) { if ($PSCmdlet.ShouldProcess($ThisSearchInfo, 'Removing Saved Search')) { $Url = 'https://api.twitter.com/1.1/saved_searches/destroy/{0}.json' -f $Id $OAuthParameters = [OAuthParameters]::new('POST',$Url) Invoke-TwitterRequest -OAuthParameters $OAuthParameters | Out-Null } } else { 'No saved search found with SearchId of {0}' -f $Id | Write-Warning } } } } #endregion #region source: \public\api\searches\Search-Tweet.ps1 function Search-Tweet { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$SearchString, [ValidateRange(1,100)] [int]$Count=15, [switch]$ExcludeEntities ) if (-Not (Test-SearchString -SearchString $SearchString)) { 'Search string {0} is not valid. Please try again.' -f $SearchString | Write-Error -ErrorAction Stop } $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/search/tweets.json', $Query ) $SearchTweet = Invoke-TwitterRequest -OAuthParameters $OAuthParameters Write-Information -MessageData $SearchTweet.search_metadata $SearchTweet.statuses } #endregion #region source: \public\api\supporting\Export-TwitterResource.ps1 function Export-TwitterResource { [CmdletBinding()] param( [Parameter(Mandatory,ParameterSetName='Configuration')] [switch]$Configuration, [Parameter(Mandatory,ParameterSetName='Languages')] [switch]$Languages ) if (-Not (Test-TwitterAuthentication -Verbose:$false)) { 'Unable to connect to Twitter. Please use Set-TwitterAuthentication to set the requried API keys and secrets. Use the -Persist switch to save the values to disk.' | Write-Error -ErrorAction Stop return } switch ($PSCmdlet.ParameterSetName) { 'Configuration' { $Resource = Get-TwitterConfiguration -Verbose:$false $ResourceSavePath = $ConfigurationSavePath } 'Languages' { $Resource = Get-TwitterLanguages -Verbose:$false $ResourceSavePath = $LanguagesSavePath } } try { $Resource | ConvertTo-Json | Set-Content -Path $ResourceSavePath -Force 'Saved Twitter {0} to {1}' -f $PSCmdlet.ParameterSetName,$ResourceSavePath | Write-Verbose } catch { $PSCmdlet.ThrowTerminatingError($_) } } #endregion #region source: \public\api\supporting\Get-TwitterAccountSettings.ps1 function Get-TwitterAccountSettings { [CmdletBinding()] param() $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/account/settings.json' ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\supporting\Get-TwitterConfiguration.ps1 function Get-TwitterConfiguration { [CmdletBinding()] param() $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/help/configuration.json' ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\supporting\Get-TwitterLanguages.ps1 function Get-TwitterLanguages { [CmdletBinding()] param() $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/help/languages.json' ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\supporting\Get-TwitterRateLimitStatus.ps1 function Get-TwitterRateLimitStatus { [CmdletBinding()] param( [ValidateSet( 'account','account_activity','admin_users','application','auth','blocks','business_experience','collections', 'contacts','custom_profiles','device','direct_messages','drafts','favorites','feedback','fleets','followers', 'friends','friendships','geo','graphql','graphql&POST','guide','help','i','labs','limiter_scalding_report_creation', 'lists','live_pipeline','live_video_stream','media','moments','mutes','oauth','safety','sandbox','saved_searches', 'search','statuses','strato','teams','traffic','trends','tweets','tweet_prompts','users','webhooks' )] [string[]]$Resources ) if ($Resources.Count -gt 0) { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/application/rate_limit_status.json', @{ 'resources' = ($Resources -join ',') } ) } else { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/application/rate_limit_status.json' ) } Invoke-TwitterRequest -OAuthParameters $OAuthParameters | Select-Object -ExpandProperty resources } #endregion #region source: \public\api\supporting\Get-TwitterUserProfileBanner.ps1 function Get-TwitterUserProfileBanner { [CmdletBinding()] param( [Parameter(Mandatory,ParameterSetName='ScreenName')] [string]$ScreenName, [Parameter(Mandatory,ParameterSetName='UserId')] [string]$UserId ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/users/profile_banner.json', $Query ) @(Invoke-TwitterRequest -OAuthParameters $OAuthParameters).sizes } #endregion #region source: \public\api\tweets\Get-Tweet.ps1 function Get-Tweet { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipeline)] [Alias('Id')] [ValidateNotNullOrEmpty()] [long]$TweetId ) $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/statuses/show.json', @{ 'id' = $TweetId } ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\tweets\Get-TweetLike.ps1 function Get-TweetLike { [CmdLetBinding(DefaultParameterSetName='ScreenName')] param( [Parameter(Mandatory,ParameterSetName='ScreenName')] [string]$ScreenName, [Parameter(Mandatory,ParameterSetName='UserId')] [string]$UserId, [ValidateRange(1,200)] [int]$Count=20, [long]$SinceId, [long]$MaxId, [switch]$ExcludeEntities ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/favorites/list.json', $Query ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\tweets\Publish-Tweet.ps1 function Publish-Tweet { [CmdletBinding(DefaultParameterSetName='Default')] param( [Parameter(Mandatory)] [string]$TweetText, [Parameter(ParameterSetName='Reply')] [long]$ReplyToTweet, [object[]]$MediaId ) # https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update # maximum of 4 pics, or 1 gif, or 1 video # count $TweetText characters # if the count is greater than allowed, suggest Send-TweetThread and fail # Get-TwitterConfiguration $Query = [hashtable]::new() $Query.Add('status', [System.Uri]::EscapeDataString($TweetText)) if ($PSCmdlet.ParameterSetName -eq 'Reply') { $Query.Add('in_reply_to_status_id', $ReplyToTweet) # this will use the tweet id to get the screen_name and append it to the @mentions until @mentions have reached the limit. $Query.Add('auto_populate_reply_metadata', 'true') } if ($MediaId.Count -gt 0) { $Query.Add('media_ids', ($MediaId -join ',')) } $OAuthParameters = [OAuthParameters]::new( 'POST', 'https://api.twitter.com/1.1/statuses/update.json', $Query ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\tweets\Set-Retweet.ps1 function Set-Retweet { [CmdletBinding(DefaultParameterSetName='Retweet')] param( [Parameter(Mandatory)] [long]$Id, [Parameter(ParameterSetName='Retweet')] [switch]$Retweet, [Parameter(ParameterSetName='Unretweet')] [switch]$Unretweet ) if ($PSCmdlet.ParameterSetName -eq 'Retweet') { $BaseUri = 'https://api.twitter.com/1.1/statuses/retweet/{0}.json' -f $Id } else { $BaseUri = 'https://api.twitter.com/1.1/statuses/unretweet/{0}.json' -f $Id } $OAuthParameters = [OAuthParameters]::new('POST',$BaseUri) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\tweets\Set-TweetLike.ps1 function Set-TweetLike { [CmdletBinding(DefaultParameterSetName='Like')] param( [Parameter(Mandatory)] [Alias('Id)')] [long]$TweetId, [Parameter(ParameterSetName='Like')] [switch]$Like, [Parameter(ParameterSetName='Unlike')] [switch]$Unlike, [switch]$ExcludeEntities ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters if ($PSCmdlet.ParameterSetName -eq 'Like') { $OAuthParameters = [OAuthParameters]::new( 'POST', 'https://api.twitter.com/1.1/favorites/create.json', $Query ) } else { $OAuthParameters = [OAuthParameters]::new( 'POST', 'https://api.twitter.com/1.1/favorites/destroy.json', $Query ) } Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\users_followers_friends\Get-TwitterBlocks.ps1 function Get-TwitterBlocks { [CmdletBinding(DefaultParameterSetName='Default')] param( [Parameter(ParameterSetName='List')] [switch]$List, [Parameter(ParameterSetName='List')] [switch]$SkipStatus, [Parameter(ParameterSetName='List')] [switch]$ExcludeEntities ) if ($PSCmdlet.ParameterSetName -eq 'List') { $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/blocks/list.json', $Query ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue users } else { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/blocks/ids.json', @{ cursor = -1} ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue ids } } #endregion #region source: \public\api\users_followers_friends\Get-TwitterFollowers.ps1 function Get-TwitterFollowers { [CmdletBinding(DefaultParameterSetName='Default')] param( [string]$ScreenName, [string]$UserId, [int]$ResultsPerPage, [Parameter(ParameterSetName='List')] [switch]$List, [Parameter(ParameterSetName='List')] [switch]$SkipStatus, [Parameter(ParameterSetName='List')] [switch]$ExcludeEntities ) if ([string]::IsNullOrWhiteSpace($ScreenName) -and [string]::IsNullOrWhiteSpace($UserId)) { 'You must supply either a ScreenName or UserId.' | Write-Warning return } elseif ($ScreenName.Length -gt 0 -and $UserId.Length -gt 0) { 'You must supply a ScreenName or a UserId, but not both.' | Write-Warning return } if ($PSCmdlet.ParameterSetName -eq 'List') { if ($ResultsPerPage -and $ResultsPerPage -notin 1..200) { 'For a list of follower objects, you can only request up to 200 user objects per page.' | Write-Warning return } else { $ResultsPerPage = 50 } } else { if ($ResultsPerPage -and $ResultsPerPage -notin 1..5000) { 'For list of follower ids, you can only request up to 5000 user is per page.' | Write-Warning return } else { $ResultsPerPage = 5000 } } if ($PSCmdlet.ParameterSetName -eq 'List') { $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/followers/list.json', $Query ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue users } else { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/followers/ids.json', @{ cursor = -1} ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue ids } } #endregion #region source: \public\api\users_followers_friends\Get-TwitterFriends.ps1 function Get-TwitterFriends { [CmdletBinding(DefaultParameterSetName='Default')] param( [string]$ScreenName, [string]$UserId, [int]$ResultsPerPage, [Parameter(ParameterSetName='List')] [switch]$List, [Parameter(ParameterSetName='List')] [switch]$SkipStatus, [Parameter(ParameterSetName='List')] [switch]$ExcludeEntities ) if ([string]::IsNullOrWhiteSpace($ScreenName) -and [string]::IsNullOrWhiteSpace($UserId)) { 'You must supply either a ScreenName or UserId' | Write-Warning return } elseif ($ScreenName.Length -gt 0 -and $UserId.Length -gt 0) { 'You must supply a ScreenName or a UserId, but not both' | Write-Warning return } if ($PSCmdlet.ParameterSetName -eq 'List') { if ($ResultsPerPage -and $ResultsPerPage -notin 1..200) { 'For a list of follower objects, you can only request up to 200 user objects per page.' | Write-Warning return } else { $ResultsPerPage = 100 } } else { if ($ResultsPerPage -and $ResultsPerPage -notin 1..5000) { 'For list of follower ids, you can only request up to 5000 user is per page.' | Write-Warning return } else { $ResultsPerPage = 5000 } } if ($PSCmdlet.ParameterSetName -eq 'List') { $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/friends/list.json', $Query ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue users } else { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/friends/ids.json', @{ cursor = -1} ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue ids } } #endregion #region source: \public\api\users_followers_friends\Get-TwitterFriendship.ps1 function Get-TwitterFriendship { [CmdletBinding(DefaultParameterSetName='LookupScreenName')] param( [Parameter(Mandatory,ValueFromPipeline,ParameterSetName='LookupScreenName')] [ValidateCount(1,100)] [string[]]$ScreenName, [Parameter(Mandatory,ValueFromPipeline,ParameterSetName='LookupUserId')] [ValidateCount(1,100)] [int[]]$UserId, [Parameter(Mandatory,ParameterSetName='ShowScreenName')] [string]$SourceScreenName, [Parameter(Mandatory,ParameterSetName='ShowUserId')] [int]$SourceUserId, [Parameter(Mandatory,ParameterSetName='ShowScreenName')] [string]$TargetScreenName, [Parameter(Mandatory,ParameterSetName='ShowUserId')] [int]$TargetUserId, [Parameter(ParameterSetName='Incoming')] [switch]$Incoming, [Parameter(ParameterSetName='Pending')] [switch]$Pending, [Parameter(ParameterSetName='NoRetweets')] [switch]$NoRetweets ) switch -Regex ($PSCmdlet.ParameterSetName) { 'Lookup' { $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/friendships/lookup.json', $Query ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } 'Show' { $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/friendships/show.json', $Query ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } 'Incoming' { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/friendships/incoming.json', @{ cursor = -1 } ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue ids } 'Pending' { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/friendships/outgoing.json', @{ cursor = -1 } ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue ids } 'NoRetweets' { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/friendships/no_retweets/ids.json' ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } } } #endregion #region source: \public\api\users_followers_friends\Get-TwitterMutedUser.ps1 function Get-TwitterMutedUser { [CmdletBinding(DefaultParameterSetName='Default')] param( [Parameter(ParameterSetName='List')] [switch]$List, [Parameter(ParameterSetName='List')] [switch]$SkipStatus, [Parameter(ParameterSetName='List')] [switch]$ExcludeEntities ) if ($PSCmdlet.ParameterSetName -eq 'List') { $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/mutes/users/list.json', $Query ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue users } else { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/mutes/users/ids.json', @{ cursor = -1} ) Invoke-TwitterCursorRequest -OAuthParameters $OAuthParameters -ReturnValue ids } } #endregion #region source: \public\api\users_followers_friends\Get-TwitterTimeline.ps1 function Get-TwitterTimeline { [CmdletBinding()] param( [Parameter(ParameterSetName='Mentions')] [switch]$MentionsTimeline, [Parameter(ParameterSetName='Home')] [switch]$HomeTimeline, [Parameter(ParameterSetName='User')] [string]$ScreenName, [Parameter(ParameterSetName='User')] [string]$UserId, [Parameter(ParameterSetName='User')] [switch]$ExcludeRetweets, [ValidateRange(1,200)] [int]$Count = 20 ) # since_id # max_id # trim_user # include_entities # count - hardcoded to 200 switch ($PSCmdlet.ParameterSetName) { 'Mentions' { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/statuses/mentions_timeline.json', @{'count' = $Count} ) } 'User' { if ($PSCmdlet.ParameterSetName -eq 'ScreenName') { $Query = @{'count' = $Count; 'screen_name' = $ScreenName} } else { $Query = @{'count' = $Count; 'user_id' = $UserId} } $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/statuses/user_timeline.json', $Query ) } 'Home' { $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/statuses/home_timeline.json', @{'count' = $Count} ) } } Invoke-TwitterRequest -OAuthParameters $OAuthParameters | ConvertTo-Json -Depth 20 | ConvertFrom-Json } #endregion #region source: \public\api\users_followers_friends\Get-TwitterUser.ps1 function Get-TwitterUser { [CmdletBinding()] param( [Parameter(Mandatory,ParameterSetName='ScreenName')] [string]$ScreenName, [Parameter(Mandatory,ParameterSetName='UserId')] [string]$UserId, [switch]$ExcludeEntities ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/users/show.json', $Query ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters } #endregion #region source: \public\api\users_followers_friends\Get-TwitterUserList.ps1 function Get-TwitterUserList { [CmdletBinding()] param( [ValidateCount(1,100)] [string[]]$ScreenName, [ValidateCount(1,100)] [string[]]$UserId, [switch]$ExcludeEntities ) $Query = New-TwitterQuery -ApiParameters $PSBoundParameters $OAuthParameters = [OAuthParameters]::new( 'GET', 'https://api.twitter.com/1.1/users/lookup.json', $Query ) Invoke-TwitterRequest -OAuthParameters $OAuthParameters | ConvertTo-Json -Depth 20 | ConvertFrom-Json } #endregion #region source: \public\helpers\Get-TwitterApiEndpoint.ps1 function Get-TwitterApiEndpoint { [CmdLetBinding(DefaultParameterSetName='Resource')] param( [Parameter(ParameterSetName='Resource')] [ValidateSet( 'account','application','blocks','direct_messages','favorites','followers', 'friends','friendships','help','lists','media','mutes','oauth2','saved_searches', 'search','statuses','users' )] [string[]]$Resource, [Parameter(ParameterSetName='Command')] [string]$Command ) $ParameterFormat = 'Name','PSParameter','Implemented','Required','Description','DefaultValue', 'MinValue','MaxValue','Example' $EndpointFormat = @{l='Endpoint';e={ '{0} {1}' -f $_.Method.ToUpper(), $_.Resource}},'Function', 'ApiVersion','Resource','Method','Uri','ApiReference','Description','Iteration', @{l='Parameters';e={ foreach ($Param in $_.Parameters) { [PSCustomObject]$Param | Select-Object $ParameterFormat } }} $TwitterApiEndpoints = Get-Content -Path $ApiEndpointsPath -Raw | ConvertFrom-Json -Depth 20 -AsHashtable switch ($PSCmdlet.ParameterSetName) { 'Resource' { if ($PSBoundParameters.ContainsKey('Resource')) { $Resource | ForEach-Object { $TwitterApiEndpoints.$_.Values | Select-Object $EndpointFormat } } else { $TwitterApiEndpoints.values.values | Select-Object $EndpointFormat } } 'Command' { $TwitterApiEndpoints.Values.Values.Where{$_.Function -eq $Command} | ForEach-Object { [PSCustomObject]$_ | Select-Object $EndpointFormat } } } } #endregion #region source: \public\helpers\Get-TwitterHistory.ps1 function Get-TwitterHistory { [CmdletBinding(DefaultParameterSetName='Default')] param( [Parameter(ParameterSetName='First')] [ValidateRange(1,[int]::MaxValue)] [int]$First, [Parameter(ParameterSetName='Last')] [ValidateRange(1,[int]::MaxValue)] [int]$Last ) switch ($PSCmdlet.ParameterSetName) { 'First' { $TwitterHistoryList | Select-Object -First $First } 'Last' { $TwitterHistoryList | Select-Object -Last $Last } default { $TwitterHistoryList } } } #endregion #region source: \public\helpers\Get-TwitterRateLimitWarning.ps1 function Get-TwitterRateLimitWarning { if ($script:RateLimitWarning) { 'RateLimitWarning is set to Enabled' } else { 'RateLimitWarning is set to Disabled' } } #endregion #region source: \public\helpers\Set-TwitterRateLimitWarning.ps1 function Set-TwitterRateLimitWarning { [CmdletBinding()] param( [Parameter(Mandatory,ParameterSetName='Enable')] [switch]$Enable, [Parameter(Mandatory,ParameterSetName='Disable')] [switch]$Disable ) if ($PSCmdlet.ParameterSetName -eq 'Enable') { if ($RateLimitWarning) { 'RateLimitWarning already set to Enable' } else { $script:RateLimitWarning = $true 'RateLimitWarning set to Enable' } } else { if ($RateLimitWarning) { $script:RateLimitWarning = $false 'RateLimitWarning set to Disable' } else { 'RateLimitWarning already set to Disable' } } } #endregion #region source: \public\helpers\Test-SearchString.ps1 function Test-SearchString { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$SearchString ) if ([System.Uri]::EscapeDataString($SearchString).Length -gt 500) { $false } else { $true } } #endregion #region source: \append\InitializeClient.ps1 # import disk resource Import-TwitterResource -ErrorMapping Import-TwitterAuthentication Import-TwitterResource -Configuration Import-TwitterResource -Languages #endregion |