diff --git a/internal/captcha/audio.go b/internal/captcha/audio.go new file mode 100644 index 0000000..aaf83e7 --- /dev/null +++ b/internal/captcha/audio.go @@ -0,0 +1,25 @@ +package captcha + +import ( + "context" + "strings" + + "github.com/lukaszraczylo/captcha-solver-mcp/internal/llm" +) + +// AudioResult is the transcription answer. +type AudioResult struct { + Text string `json:"text"` + Confidence float64 `json:"confidence"` +} + +// SolveAudio transcribes an audio captcha. Confidence is fixed at 1.0 for +// transcription providers (which do not return token-level confidence here); +// the raw transcript is trimmed. +func SolveAudio(ctx context.Context, c llm.Client, audio []byte, filename, language string) (AudioResult, error) { + txt, err := c.Transcribe(ctx, audio, filename, llm.Options{Language: language}) + if err != nil { + return AudioResult{}, err + } + return AudioResult{Text: strings.TrimSpace(txt), Confidence: 1.0}, nil +} diff --git a/internal/captcha/audio_test.go b/internal/captcha/audio_test.go new file mode 100644 index 0000000..6b65ff3 --- /dev/null +++ b/internal/captcha/audio_test.go @@ -0,0 +1,29 @@ +package captcha + +import ( + "context" + "errors" + "testing" + + "github.com/lukaszraczylo/captcha-solver-mcp/internal/llm" +) + +func TestSolveAudio(t *testing.T) { + f := &llm.Fake{TranscribeResp: " forty two "} + res, err := SolveAudio(context.Background(), f, []byte("RIFF"), "a.wav", "en") + if err != nil { + t.Fatal(err) + } + if res.Text != "forty two" { + t.Fatalf("got %q", res.Text) + } +} + +func TestSolveAudioUnsupported(t *testing.T) { + f := &llm.Fake{TranscribeErr: llm.ErrUnsupported{Provider: "anthropic"}} + _, err := SolveAudio(context.Background(), f, []byte{1}, "a.wav", "") + var u llm.ErrUnsupported + if !errors.As(err, &u) { + t.Fatalf("want ErrUnsupported, got %v", err) + } +}