101 lines
5.3 KiB
PowerShell
101 lines
5.3 KiB
PowerShell
<#
|
||
S8:人工链路本身(不经第三方 chatbot)。
|
||
SCRM 开工(SetSetting IsEnable=false + UpdateCSAvailable 3)→ 客服上线(UserReg + 额度池)
|
||
→ 新 IG 用户发消息 → 人工工单,SCRM 在 WebSocket 上收到推送 → 指派 → 打开(验证信息 + 已读)
|
||
→ 以客服身份回复 → IG 用户收到 → 结束工单 → SCRM 下班(切回机器人、在线 0),不影响 S1。
|
||
前提:.\loop.ps1 up 各角色都健康。每次用新的 IGSID,保证建出新工单。
|
||
通过退出 0,失败退出 1 并打印记录;两种情况都在 run\reports\ 生成场景报告(观察台在跑时)。
|
||
用法:.\scenarios\s8-human-chain.ps1 [-TimeoutSec 60] [-KeepShift] [-NoReport]
|
||
#>
|
||
param([int]$TimeoutSec = 60, [switch]$KeepShift, [switch]$NoReport)
|
||
$ErrorActionPreference = 'Stop'
|
||
$ProgressPreference = 'SilentlyContinue'
|
||
$LoopNoReport = [bool]$NoReport
|
||
. (Join-Path $PSScriptRoot '_lib.ps1')
|
||
$Cfg = $LoopCfg
|
||
$ig = "http://127.0.0.1:$($Cfg.Ports.IgMock)"
|
||
$sc = "http://127.0.0.1:$($Cfg.Ports.ScrmMock)"
|
||
|
||
function Post([string]$url, $body) {
|
||
$bytes = [Text.Encoding]::UTF8.GetBytes(($body | ConvertTo-Json -Compress -Depth 6))
|
||
try { return Invoke-RestMethod $url -Method Post -ContentType 'application/json; charset=utf-8' -Body $bytes }
|
||
catch {
|
||
# 动作失败时模拟方回 502,正文里带着每一步的记录
|
||
$resp = $_.Exception.Response
|
||
if (-not $resp) { throw }
|
||
$reader = New-Object IO.StreamReader($resp.GetResponseStream(), [Text.Encoding]::UTF8)
|
||
return ($reader.ReadToEnd() | ConvertFrom-Json)
|
||
}
|
||
}
|
||
|
||
function Fail([string]$why) {
|
||
Write-Host "S8 失败:$why" -ForegroundColor Red
|
||
Write-Host '--- SCRM 模拟记录(最近 15 条)'
|
||
(Invoke-RestMethod "$sc/_api/state").events | Select-Object -First 15 | ForEach-Object { ($_ | ConvertTo-Json -Depth 8 -Compress) } | Out-Host
|
||
Write-Host '--- IG 模拟记录'
|
||
(Invoke-RestMethod "$ig/_api/state").events | ForEach-Object { "{0} {1} {2}" -f $_.kind, $_.igsid, $_.summary } | Out-Host
|
||
Add-LoopStep $why $false
|
||
if (-not $KeepShift) { Post "$sc/_api/shift" @{ on = $false } | Out-Null }
|
||
Save-LoopReport -Passed $false -Reason $why
|
||
exit 1
|
||
}
|
||
|
||
function Step([string]$name, [string]$action, $body) {
|
||
$r = Post "$sc/_api/$action" $body
|
||
if (-not $r.ok) { Fail "$name($action)未成功:$($r.error) $((@($r.calls) | ForEach-Object { $_.api + ' → ' + ($_.response.body.msg) }) -join ';')" }
|
||
Add-LoopStep $name $true ((@($r.calls) | ForEach-Object { $_.api }) -join ' + ')
|
||
return $r
|
||
}
|
||
|
||
function Wait-For([string]$what, [scriptblock]$probe) {
|
||
$deadline = (Get-Date).AddSeconds($TimeoutSec)
|
||
while ((Get-Date) -lt $deadline) {
|
||
$hit = & $probe
|
||
if ($hit) { return $hit }
|
||
Start-Sleep -Milliseconds 500
|
||
}
|
||
Fail "$TimeoutSec 秒内没等到:$what"
|
||
}
|
||
|
||
Start-LoopRun 'S8' 'SCRM 人工链路'
|
||
$ws = (Invoke-RestMethod "$sc/_api/state").state.ws
|
||
if ($ws.status -ne 'logged-in') { Fail "SCRM 模拟的 WebSocket 没登录($($ws.status) $($ws.lastError))" }
|
||
|
||
Invoke-RestMethod "$sc/_api/clear" -Method Post | Out-Null
|
||
Invoke-RestMethod "$ig/_api/clear" -Method Post | Out-Null
|
||
|
||
Step 'SCRM 开工(切人工、在线 3)' 'shift' @{ on = $true } | Out-Null
|
||
Step '客服上线' 'agent-online' @{} | Out-Null
|
||
|
||
$igsid = 'loop-s8-' + [guid]::NewGuid().ToString('N').Substring(0, 8)
|
||
$sent = Post "$ig/_api/send" @{ kind = 'text'; igsid = $igsid; text = 'S8 想找人工' }
|
||
if ($sent.event.response.status -ne 200) { Fail "iXserver 收 webhook 返回 $($sent.event.response.status)" }
|
||
Add-LoopStep '新 IG 用户发消息' $true "$igsid HTTP 200"
|
||
|
||
# 推送报文没有统一类型字段,按 enduser_openid = IGSID 认出这张新工单
|
||
$ticketId = Wait-For 'SCRM 收到这位 IG 用户的工单推送' {
|
||
$push = @((Invoke-RestMethod "$sc/_api/state").events | Where-Object { $_.kind -eq 'push' -and (($_.parsed | ConvertTo-Json -Depth 8 -Compress) -match [regex]::Escape($igsid)) }) | Select-Object -First 1
|
||
if ($push) { $push.ticketIds | Select-Object -First 1 }
|
||
}
|
||
Set-LoopTicket $ticketId
|
||
Add-LoopStep 'SCRM 收到工单推送' $true "ticket $ticketId"
|
||
|
||
Step '指派给客服' 'assign' @{ ticket_id = $ticketId } | Out-Null
|
||
Step '打开工单(验证信息 + 已读)' 'open' @{ ticket_id = $ticketId } | Out-Null
|
||
$replyText = 'S8 客服回复 ' + [guid]::NewGuid().ToString('N').Substring(0, 6)
|
||
Step '客服回复' 'reply' @{ ticket_id = $ticketId; text = $replyText } | Out-Null
|
||
|
||
$out = Wait-For "IG 用户收到「$replyText」" {
|
||
@((Invoke-RestMethod "$ig/_api/state").events | Where-Object { $_.kind -eq 'outbound' -and $_.igsid -eq $igsid -and $_.summary -eq $replyText }) | Select-Object -First 1
|
||
}
|
||
if (@($out.issues).Count) { Fail "出站请求有问题:$($out.issues -join ';')" }
|
||
if ($out.response.status -ne 200) { Fail "Graph 应答 $($out.response.status)(IG 模拟档位 $($out.response.mode)),用户实际没收到" }
|
||
Add-LoopStep 'IG 用户收到客服回复' $true $replyText
|
||
|
||
Step '结束工单' 'complete' @{ ticket_id = $ticketId } | Out-Null
|
||
if (-not $KeepShift) { Step 'SCRM 下班(切回机器人、在线 0)' 'shift' @{ on = $false } | Out-Null }
|
||
|
||
Write-Host "S8 通过:$igsid → 人工工单 $ticketId → SCRM 收到推送、指派、回复 → IG 收到「$replyText」→ 已结束" -ForegroundColor Green
|
||
Save-LoopReport -Passed $true
|
||
exit 0
|