70 lines
2.9 KiB
Go
70 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"git.ipao.vip/rogee/go-sip/internal/ai"
|
|
)
|
|
|
|
const maxApprovedMockScenarioBytes = 4 << 20
|
|
|
|
type approvedMockScenario struct {
|
|
InboundPCM16 []byte
|
|
Script ai.ApprovedMockScript
|
|
MaxWAVBytes int64
|
|
ExpectedRecording bool
|
|
Outcome string
|
|
ReasonMessage string
|
|
}
|
|
|
|
// loadApprovedMockScenario reads only explicit synthetic media facts. It does
|
|
// not read or override the AI business configuration delivered by Dispatcher.
|
|
func loadApprovedMockScenario(path string) (approvedMockScenario, error) {
|
|
if strings.TrimSpace(path) == "" {
|
|
return approvedMockScenario{}, errors.New("AGENT_MOCK_SCENARIO_FILE is required")
|
|
}
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return approvedMockScenario{}, errors.New("AGENT_MOCK_SCENARIO_FILE cannot be opened")
|
|
}
|
|
defer file.Close()
|
|
data, err := io.ReadAll(io.LimitReader(file, maxApprovedMockScenarioBytes+1))
|
|
if err != nil || len(data) > maxApprovedMockScenarioBytes {
|
|
return approvedMockScenario{}, errors.New("AGENT_MOCK_SCENARIO_FILE exceeds its size limit or cannot be read")
|
|
}
|
|
var encoded struct {
|
|
InboundPCM16 *string `json:"inbound_pcm16"`
|
|
Script *ai.ApprovedMockScript `json:"script"`
|
|
MaxWAVBytes *int64 `json:"max_wav_bytes"`
|
|
ExpectedRecording *bool `json:"expected_recording"`
|
|
Outcome *string `json:"outcome"`
|
|
ReasonMessage *string `json:"reason_message"`
|
|
}
|
|
decoder := json.NewDecoder(bytes.NewReader(data))
|
|
decoder.DisallowUnknownFields()
|
|
if err := decoder.Decode(&encoded); err != nil {
|
|
return approvedMockScenario{}, errors.New("AGENT_MOCK_SCENARIO_FILE has invalid fields")
|
|
}
|
|
if err := decoder.Decode(new(any)); err != io.EOF {
|
|
return approvedMockScenario{}, errors.New("AGENT_MOCK_SCENARIO_FILE contains extra data")
|
|
}
|
|
if encoded.InboundPCM16 == nil || encoded.Script == nil || encoded.MaxWAVBytes == nil || encoded.ExpectedRecording == nil || encoded.Outcome == nil || encoded.ReasonMessage == nil {
|
|
return approvedMockScenario{}, errors.New("AGENT_MOCK_SCENARIO_FILE requires explicit media, script, outcome and recording facts")
|
|
}
|
|
pcm, err := base64.StdEncoding.DecodeString(*encoded.InboundPCM16)
|
|
if err != nil || len(encoded.Script.Turns) == 0 || *encoded.MaxWAVBytes <= 44 || strings.TrimSpace(*encoded.ReasonMessage) == "" ||
|
|
(*encoded.Outcome != "answered" && *encoded.Outcome != "no_answer" && *encoded.Outcome != "failed") {
|
|
return approvedMockScenario{}, errors.New("AGENT_MOCK_SCENARIO_FILE has invalid synthetic media or outcome")
|
|
}
|
|
return approvedMockScenario{
|
|
InboundPCM16: pcm, Script: *encoded.Script, MaxWAVBytes: *encoded.MaxWAVBytes,
|
|
ExpectedRecording: *encoded.ExpectedRecording, Outcome: *encoded.Outcome, ReasonMessage: *encoded.ReasonMessage,
|
|
}, nil
|
|
}
|