public/Invoke-XPost.ps1
function Invoke-XPost { <# .EXAMPLE Invoke-XPost -ConsumerKey $ConsumerKey -ConsumerSecret $ConsumerSecret -Token $Token -TokenSecret $TokenSecret -Text $Text #> [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ConsumerKey, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ConsumerSecret, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Text, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Token, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$TokenSecret ) process{ $ErrorActionPreference = 'Stop' try { $oauth_consumer_key = $ConsumerKey # API Key (Consumer Key) $oauth_consumer_secret = $ConsumerSecret # API Key Secret (Consumer Secret) $oauth_token = $Token # Access Token $oauth_token_secret = $TokenSecret # Access Token Secret # Enable modern TLS protocols to avoid connection failures [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls13 $oauth_nonce = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes([System.DateTime]::Now.Ticks.ToString())) $ts = [System.DateTime]::UtcNow - [System.DateTime]::UnixEpoch $oauth_timestamp = [System.Convert]::ToInt64($ts.TotalSeconds).ToString() # Build the signature base string (no body params for JSON POST) $signature_base = 'POST&' + [System.Uri]::EscapeDataString('https://api.twitter.com/2/tweets') + '&' $signature_base += [System.Uri]::EscapeDataString("oauth_consumer_key=$oauth_consumer_key&") $signature_base += [System.Uri]::EscapeDataString("oauth_nonce=$oauth_nonce&") $signature_base += [System.Uri]::EscapeDataString("oauth_signature_method=HMAC-SHA1&") $signature_base += [System.Uri]::EscapeDataString("oauth_timestamp=$oauth_timestamp&") $signature_base += [System.Uri]::EscapeDataString("oauth_token=$oauth_token&") $signature_base += [System.Uri]::EscapeDataString("oauth_version=1.0") $signature_key = [System.Uri]::EscapeDataString($oauth_consumer_secret) + '&' + [System.Uri]::EscapeDataString($oauth_token_secret) $hmacsha1 = New-Object System.Security.Cryptography.HMACSHA1 $hmacsha1.Key = [System.Text.Encoding]::ASCII.GetBytes($signature_key) $oauth_signature = [System.Convert]::ToBase64String($hmacsha1.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($signature_base))) # Build the Authorization header $oauth_authorization = 'OAuth ' $oauth_authorization += 'oauth_consumer_key="' + [System.Uri]::EscapeDataString($oauth_consumer_key) + '",' $oauth_authorization += 'oauth_nonce="' + [System.Uri]::EscapeDataString($oauth_nonce) + '",' $oauth_authorization += 'oauth_signature="' + [System.Uri]::EscapeDataString($oauth_signature) + '",' $oauth_authorization += 'oauth_signature_method="HMAC-SHA1",' $oauth_authorization += 'oauth_timestamp="' + [System.Uri]::EscapeDataString($oauth_timestamp) + '",' $oauth_authorization += 'oauth_token="' + [System.Uri]::EscapeDataString($oauth_token) + '",' $oauth_authorization += 'oauth_version="1.0"' # Prepare JSON body $bodyJson = @{ text = $Text } | ConvertTo-Json -Compress $post_body = [System.Text.Encoding]::UTF8.GetBytes($bodyJson) # Send the request [System.Net.HttpWebRequest] $request = [System.Net.WebRequest]::Create('https://api.twitter.com/2/tweets') $request.Method = 'POST' $request.Headers.Add('Authorization', $oauth_authorization) $request.ContentType = 'application/json' $request.ContentLength = $post_body.Length $bodyStream = $request.GetRequestStream() $bodyStream.Write($post_body, 0, $post_body.Length) $bodyStream.Flush() $bodyStream.Close() $response = $request.GetResponse() # Get rate limit headers $rateLimit = $response.Headers["x-rate-limit-limit"] $rateRemaining = $response.Headers["x-rate-limit-remaining"] $rateReset = $response.Headers["x-rate-limit-reset"] $resetTime = if ($rateReset) { [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($rateReset)) } else { "N/A" } # Read response body (for success details, e.g., tweet ID) $stream = New-Object System.IO.StreamReader($response.GetResponseStream()) $body = $stream.ReadToEnd() $stream.Close() Write-Output "Post successful! Status: $($response.StatusCode)" Write-Output "Response Body: $body" Write-Output "Rate Limits: Limit=$rateLimit, Remaining=$rateRemaining, Resets At=$resetTime (local time)" $response.Close() } catch { $err = $_ # Unwrap nested exceptions to find the innermost one (likely WebException) $ex = $err.Exception while ($ex.InnerException) { $ex = $ex.InnerException } $errResp = $ex.Response Write-Output "Exception Type: $($err.Exception.GetType().FullName)" Write-Output "Exception Message: $($err.Exception.Message)" if ($err.Exception.InnerException) { Write-Output "Inner Exception: $($err.Exception.InnerException.Message)" } if ($errResp) { # Get rate limit headers from error response (if present) $rateLimit = $errResp.Headers["x-rate-limit-limit"] $rateRemaining = $errResp.Headers["x-rate-limit-remaining"] $rateReset = $errResp.Headers["x-rate-limit-reset"] $resetTime = if ($rateReset) { [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($rateReset)) } else { "N/A" } # Read error response body (e.g., for JSON error details) $errStream = New-Object System.IO.StreamReader($errResp.GetResponseStream()) $errBody = $errStream.ReadToEnd() $errStream.Close() Write-Output "Post failed! HTTP Status: $($errResp.StatusCode) $($errResp.StatusDescription)" Write-Output "Error Body: $errBody" Write-Output "Rate Limits: Limit=$rateLimit, Remaining=$rateRemaining, Resets At=$resetTime (local time)" } else { Write-Output "Post failed! Non-HTTP error (e.g., connection issue)." } # if } # try catch } # process } # function |