package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "net/http" "net/url" "os" "strings" "time" volcbase "github.com/volcengine/volc-sdk-golang/base" ) type responseEnvelope struct { ResponseMetadata struct { Error *struct { Code string `json:"Code"` Message string `json:"Message"` } `json:"Error"` } `json:"ResponseMetadata"` } func main() { akFile := flag.String("ak-file", "", "file containing the Volcengine AccessKey ID") skFile := flag.String("sk-file", "", "file containing the Volcengine SecretAccessKey") endpoint := flag.String("endpoint", "https://rtc.volcengineapi.com", "RTC OpenAPI endpoint") rtcAppID := flag.String("rtc-app-id", "", "RTC application ID used for staged validation probes") templateFile := flag.String("template-file", "", "voice-chat template bundle used for ProviderParams probes") roomID := flag.String("room-id", "", "RTC room ID to inspect without starting a task") flag.Parse() if *akFile == "" || *skFile == "" { panic("-ak-file and -sk-file are required") } ak := readSecret(*akFile) sk := readSecret(*skFile) credentials := volcbase.Credentials{AccessKeyID: ak, SecretAccessKey: sk, Service: "rtc", Region: "cn-north-1"} client := &http.Client{Timeout: 15 * time.Second} if *rtcAppID != "" && *roomID != "" { queryRoom(client, credentials, *endpoint, *rtcAppID, *roomID) return } for _, version := range []string{"2024-06-01", "2024-12-01", "2025-06-01"} { probe(client, credentials, *endpoint, version, "empty", map[string]any{}) } if *rtcAppID != "" { version := "2024-12-01" identity := map[string]any{ "AppId": *rtcAppID, "RoomId": "probe-room", "TaskId": "probe-task", } agent := map[string]any{ "TargetUserId": []string{"probe-user"}, "UserId": "probe-agent", } probe(client, credentials, *endpoint, version, "app-only", map[string]any{"AppId": *rtcAppID}) probe(client, credentials, *endpoint, version, "identity-only", map[string]any{ "AppId": *rtcAppID, "RoomId": "probe-room", "TaskId": "probe-task", }) probe(client, credentials, *endpoint, version, "empty-config", map[string]any{ "AppId": *rtcAppID, "RoomId": "probe-room", "TaskId": "probe-task", "Config": map[string]any{}, "AgentConfig": map[string]any{}, }) probe(client, credentials, *endpoint, version, "agent-valid", merge(identity, map[string]any{ "Config": map[string]any{}, "AgentConfig": agent, })) probe(client, credentials, *endpoint, version, "s2s-shell", merge(identity, map[string]any{ "Config": map[string]any{"S2SConfig": map[string]any{}}, "AgentConfig": agent, })) probe(client, credentials, *endpoint, version, "s2s-provider", merge(identity, map[string]any{ "Config": map[string]any{"S2SConfig": map[string]any{ "Provider": "volcano", "OutputMode": 0, }}, "AgentConfig": agent, })) probe(client, credentials, *endpoint, version, "s2s-params-empty", merge(identity, map[string]any{ "Config": map[string]any{"S2SConfig": map[string]any{ "Provider": "volcano", "OutputMode": 0, "ProviderParams": map[string]any{}, }}, "AgentConfig": agent, })) if *templateFile != "" { providerParams := readProviderParams(*templateFile) for _, item := range []struct { label string keys []string }{ {label: "params-app", keys: []string{"app"}}, {label: "params-dialog", keys: []string{"dialog"}}, {label: "params-asr", keys: []string{"asr"}}, {label: "params-tts", keys: []string{"tts"}}, {label: "params-full", keys: []string{"app", "dialog", "asr", "tts"}}, } { selected := make(map[string]any, len(item.keys)) for _, key := range item.keys { if value, ok := providerParams[key]; ok { selected[key] = value } } probeIdentity := merge(identity, map[string]any{"TaskId": "probe-" + item.label}) payload := merge(probeIdentity, map[string]any{ "Config": map[string]any{"S2SConfig": map[string]any{ "Provider": "volcano", "OutputMode": 0, "ProviderParams": selected, }}, "AgentConfig": agent, }) if probeAction(client, credentials, *endpoint, version, item.label, "StartVoiceChat", payload) { probeAction(client, credentials, *endpoint, version, item.label+"-cleanup", "StopVoiceChat", probeIdentity) } } fullTemplate := readHybridTemplate(*templateFile) fullIdentity := merge(identity, map[string]any{"TaskId": "probe-full-template"}) fullTemplate = merge(fullTemplate, fullIdentity) fullAgent, _ := fullTemplate["AgentConfig"].(map[string]any) fullAgent["TargetUserId"] = []string{"probe-user"} fullAgent["UserId"] = "probe-agent" if probeAction(client, credentials, *endpoint, version, "full-template", "StartVoiceChat", fullTemplate) { probeAction(client, credentials, *endpoint, version, "full-template-cleanup", "StopVoiceChat", fullIdentity) } } probeAction(client, credentials, *endpoint, version, "cleanup", "StopVoiceChat", identity) } } func queryRoom(client *http.Client, credentials volcbase.Credentials, endpoint, appID, roomID string) { target, err := url.Parse(endpoint) if err != nil { panic(err) } query := target.Query() query.Set("Action", "GetRoomOnlineUsers") query.Set("Version", "2023-11-01") query.Set("AppId", appID) query.Set("RoomId", roomID) query.Set("Property", "false") target.RawQuery = query.Encode() request, err := http.NewRequest(http.MethodGet, target.String(), nil) if err != nil { panic(err) } request.Host = target.Host request.Header.Set("Accept", "application/json") credentials.Sign(request) response, err := client.Do(request) if err != nil { panic(err) } defer response.Body.Close() body, err := io.ReadAll(io.LimitReader(response.Body, 1<<20)) if err != nil { panic(err) } var decoded map[string]any if err := json.Unmarshal(body, &decoded); err != nil { panic(err) } encoded, _ := json.Marshal(decoded) fmt.Printf("room-status=%d response=%s\n", response.StatusCode, encoded) } func readProviderParams(path string) map[string]any { template := readHybridTemplate(path) config, _ := template["Config"].(map[string]any) s2s, _ := config["S2SConfig"].(map[string]any) params, _ := s2s["ProviderParams"].(map[string]any) if params == nil { panic("template does not contain s2s_hybrid Config.S2SConfig.ProviderParams") } return params } func readHybridTemplate(path string) map[string]any { raw, err := os.ReadFile(path) if err != nil { panic(err) } var bundle map[string]any if err := json.Unmarshal(raw, &bundle); err != nil { panic(err) } templates, _ := bundle["templates"].(map[string]any) template, _ := templates["s2s_hybrid"].(map[string]any) if template == nil { panic("template does not contain s2s_hybrid") } return template } func merge(base map[string]any, extra map[string]any) map[string]any { result := make(map[string]any, len(base)+len(extra)) for key, value := range base { result[key] = value } for key, value := range extra { result[key] = value } return result } func readSecret(path string) string { value, err := os.ReadFile(path) if err != nil { panic(err) } return strings.TrimSpace(string(value)) } func probe(client *http.Client, credentials volcbase.Credentials, endpoint, version, label string, payload map[string]any) { probeAction(client, credentials, endpoint, version, label, "StartVoiceChat", payload) } func probeAction(client *http.Client, credentials volcbase.Credentials, endpoint, version, label, action string, payload map[string]any) bool { target, err := url.Parse(endpoint) if err != nil { panic(err) } query := target.Query() query.Set("Action", action) query.Set("Version", version) target.RawQuery = query.Encode() bodyBytes, err := json.Marshal(payload) if err != nil { panic(err) } request, err := http.NewRequest(http.MethodPost, target.String(), bytes.NewReader(bodyBytes)) if err != nil { panic(err) } request.Host = target.Host request.Header.Set("Content-Type", "application/json") request.Header.Set("Accept", "application/json") credentials.Sign(request) response, err := client.Do(request) if err != nil { fmt.Printf("label=%s version=%s transport_error=%v\n", label, version, err) return false } defer response.Body.Close() body, err := io.ReadAll(io.LimitReader(response.Body, 1<<20)) if err != nil { fmt.Printf("label=%s version=%s status=%d read_error=%v\n", label, version, response.StatusCode, err) return false } var envelope responseEnvelope _ = json.Unmarshal(body, &envelope) if envelope.ResponseMetadata.Error == nil { fmt.Printf("label=%s version=%s status=%d code=none\n", label, version, response.StatusCode) return response.StatusCode >= 200 && response.StatusCode < 300 } fmt.Printf("label=%s version=%s status=%d code=%s message=%s\n", label, version, response.StatusCode, envelope.ResponseMetadata.Error.Code, envelope.ResponseMetadata.Error.Message) return false }