CQHttp.ps1
. "$PSScriptRoot\Logging.ps1" . "$PSScriptRoot\Json.ps1" . "$PSScriptRoot\DeepCopy.ps1" function Invoke-CQHttpBot { param ( [string]$ApiRoot = "http://127.0.0.1:5700", [string]$Address = "127.0.0.1:8080", # Expected value: @( @("message", callbackBlock1), @("request.friend", callbackBlock2) ) [Alias("EventHandlers")] [array[]]$EventCallbacks = @() ) $url = "http://$Address/" $listener = [System.Net.HttpListener]::new() $listener.Prefixes.Add($url) $listener.Start() if ($listener.IsListening) { Write-FormattedLog "CQHTTP bot is running on $url..." -Level Info } else { Write-FormattedLog "Failed to start HTTP listener" -Level Fatal } $bot = @{ ApiRoot = $ApiRoot.TrimEnd("/") Address = $Address Listener = $listener } $bot.CallAction = { param ( [Parameter(Mandatory = $true)] [string]$Action, [hashtable]$Params = @{} ) return Invoke-CQHttpAction -Bot $bot -Action $Action -Params $Params } $bot.Send = { param ( [Parameter(Mandatory = $true)] [hashtable]$Context, [Parameter(Mandatory = $true)] $Message ) $ctx = Copy-ObjectDeeply $Context $ctx.message = $Message return & $bot.CallAction -Action "send_msg" -Params $ctx } while ($bot.Listener.IsListening) { $event = Receive-Event -Bot $bot Write-FormattedLog "Received event: $($event.Name)" -Level Info $EventCallbacks | ForEach-Object { if (([string]$event.Name).StartsWith($_[0])) { try { & $_[1] $bot $event.Data } catch { Write-FormattedLog "An error occurred while running event callback" -Level Error Write-FormattedLog "Error: $_" -Level Error } } } } } function Receive-Event { param ( [hashtable]$Bot ) $listner = $Bot.Listener while ($listner.IsListening) { $context = $listner.GetContext() # Accept request $method = $context.Request.HttpMethod $path = $context.Request.RawUrl $body = "" if ($method -ne "POST") { $statusCode = 405 } elseif ($path -ne "/") { $statusCode = 404 } else { $statusCode = 204 $body = [System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd() } Write-AccessLog -Method $method -Path $path -StatusCode $statusCode $context.Response.StatusCode = $statusCode $context.Response.OutputStream.Close() if (-not $body) { continue } Write-FormattedLog "Received body: $body" -Level Debug try { [hashtable]$payload = $body | ConvertFrom-Json | Convert-PSObjectToHashtable } catch { continue } $postType = $payload.post_type if (-not $postType) { continue } $detailType = $payload."${postType}_type" $eventName = "${postType}.${detailType}" if ($payload.sub_type) { $eventName += ".$($payload.sub_type)" } return @{Name = [string]$eventName; Data = [hashtable]$payload} } } function Invoke-CQHttpAction { param ( [Parameter(Mandatory = $true)] [hashtable]$Bot, [Parameter(Mandatory = $true)] [string]$Action, [hashtable]$Params = @{} ) $url = "$($Bot.ApiRoot)/$Action" $body = [System.Text.Encoding]::UTF8.GetBytes(($Params | ConvertTo-Json)) return Invoke-WebRequest ` -Method Post -Uri $url ` -ContentType "application/json; charset=utf-8" ` -Body $body | ConvertFrom-Json | Convert-PSObjectToHashtable } |