Test-CentralFeedbackSender.ps1 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. [CmdletBinding()]
  2. param(
  3. [string]$ConfigPath = (Join-Path $PSScriptRoot 'config\central-feedback.config.dpapi')
  4. )
  5. $ErrorActionPreference = 'Stop'
  6. . (Join-Path $PSScriptRoot 'Common.ps1')
  7. Add-Type -AssemblyName System.Net.Http
  8. function Invoke-FeishuJson {
  9. param([System.Net.Http.HttpClient]$Client, [string]$Uri, [object]$Body)
  10. $json = $Body | ConvertTo-Json -Compress -Depth 6
  11. $content = New-Object -TypeName System.Net.Http.StringContent -ArgumentList @($json, [Text.Encoding]::UTF8, 'application/json')
  12. try {
  13. $response = $Client.PostAsync($Uri, $content).GetAwaiter().GetResult()
  14. $raw = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
  15. }
  16. finally { $content.Dispose() }
  17. if (-not $response.IsSuccessStatusCode) { throw "Feishu HTTP $([int]$response.StatusCode)" }
  18. $data = $raw | ConvertFrom-Json
  19. if ($data.code -ne 0) { throw "Feishu code $($data.code)" }
  20. return $data
  21. }
  22. $config = Get-ProtectedConfig -Path $ConfigPath
  23. if (-not $config.app_id -or -not $config.app_secret -or -not $config.chat_id) { throw 'Server configuration is incomplete.' }
  24. $fileName = 'CENTRAL-SENDER-SELF-TEST.md'
  25. $testText = "# Central feedback sender self-test`r`n`r`nThis file verifies the server-to-Feishu delivery path. It contains no project data, credentials, or business information.`r`n"
  26. $fileBytes = [Text.Encoding]::UTF8.GetBytes($testText)
  27. $client = New-Object System.Net.Http.HttpClient
  28. try {
  29. $token = Invoke-FeishuJson -Client $client -Uri 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal' -Body @{ app_id = [string]$config.app_id; app_secret = [string]$config.app_secret }
  30. $client.DefaultRequestHeaders.Authorization = New-Object -TypeName System.Net.Http.Headers.AuthenticationHeaderValue -ArgumentList @('Bearer', [string]$token.tenant_access_token)
  31. $form = New-Object System.Net.Http.MultipartFormDataContent
  32. try {
  33. $form.Add((New-Object -TypeName System.Net.Http.StringContent -ArgumentList 'stream'), 'file_type')
  34. $form.Add((New-Object -TypeName System.Net.Http.StringContent -ArgumentList @($fileName, [Text.Encoding]::UTF8)), 'file_name')
  35. $fileContent = New-Object -TypeName System.Net.Http.ByteArrayContent -ArgumentList (,$fileBytes)
  36. $fileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('application/octet-stream')
  37. $form.Add($fileContent, 'file', $fileName)
  38. $uploadResponse = $client.PostAsync('https://open.feishu.cn/open-apis/im/v1/files', $form).GetAwaiter().GetResult()
  39. $uploadRaw = $uploadResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult()
  40. }
  41. finally { $form.Dispose() }
  42. if (-not $uploadResponse.IsSuccessStatusCode) { throw "Feishu upload HTTP $([int]$uploadResponse.StatusCode)" }
  43. $upload = $uploadRaw | ConvertFrom-Json
  44. if ($upload.code -ne 0 -or [string]::IsNullOrWhiteSpace($upload.data.file_key)) { throw "Feishu upload code $($upload.code)" }
  45. $result = Invoke-FeishuJson -Client $client -Uri 'https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id' -Body @{
  46. receive_id = [string]$config.chat_id
  47. msg_type = 'file'
  48. content = (@{ file_key = [string]$upload.data.file_key; file_name = $fileName } | ConvertTo-Json -Compress)
  49. }
  50. Write-Output ([pscustomobject]@{
  51. status = 'sent'
  52. file_name = $fileName
  53. message_id = [string]$result.data.message_id
  54. sent_at = (Get-Date).ToString('o')
  55. })
  56. }
  57. finally {
  58. $client.Dispose()
  59. if ($fileBytes) { [Array]::Clear($fileBytes, 0, $fileBytes.Length) }
  60. $config.app_secret = $null
  61. }