T3 Chat STT with Whisper API (Configurable API Key)

Adds speech-to-text functionality to t3.chat using OpenAI's Whisper API with configurable API key via UI.

// ==UserScript==
// @name         T3 Chat STT with Whisper API (Configurable API Key)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Adds speech-to-text functionality to t3.chat using OpenAI's Whisper API with configurable API key via UI.
// @author       wearifulpoet
// @match        https://t3.chat/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // ********** API Key Configuration **********
    // Use localStorage to store the API key.
    // Initially, if no API key is stored, an empty string is returned.
    const getApiKey = () => localStorage.getItem("whisper_api_key") || "";
    const setApiKey = (key) => localStorage.setItem("whisper_api_key", key);
    // *********************************************

    // Create the STT button.
    const sttButton = document.createElement('button');
    sttButton.innerHTML = "🎙️ Start STT";
    sttButton.style.position = "fixed";
    sttButton.style.bottom = "20px";
    sttButton.style.right = "20px";
    sttButton.style.zIndex = "9999";
    sttButton.style.padding = "10px";
    sttButton.style.fontSize = "16px";
    document.body.appendChild(sttButton);

    // Create the API Key configuration button.
    const apiKeyButton = document.createElement('button');
    apiKeyButton.innerHTML = "API Key";
    apiKeyButton.style.position = "fixed";
    apiKeyButton.style.bottom = "20px";
    apiKeyButton.style.right = "140px";
    apiKeyButton.style.zIndex = "9999";
    apiKeyButton.style.padding = "10px";
    apiKeyButton.style.fontSize = "16px";
    document.body.appendChild(apiKeyButton);

    // Event listener to set/update the API key.
    apiKeyButton.addEventListener("click", () => {
        const currentKey = getApiKey();
        const newKey = prompt("Enter your Whisper API key:", currentKey);
        if (newKey !== null) {
            setApiKey(newKey.trim());
            alert("API key updated!");
        }
    });

    let mediaRecorder = null;
    let audioChunks = [];
    let isRecording = false;

    sttButton.addEventListener("click", async () => {
        if (!isRecording) {
            // Check if the API key has been set.
            const storedKey = getApiKey();
            if (!storedKey) {
                alert("Please set your Whisper API key using the API Key button.");
                return;
            }
            // Start recording
            sttButton.innerHTML = "Stop Recording";
            isRecording = true;
            try {
                const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
                mediaRecorder = new MediaRecorder(stream);
                audioChunks = [];

                mediaRecorder.addEventListener("dataavailable", event => {
                    if (event.data.size > 0) {
                        audioChunks.push(event.data);
                    }
                });

                mediaRecorder.addEventListener("stop", async () => {
                    const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
                    // Prepare the file to send to Whisper API.
                    const audioFile = new File([audioBlob], "audio.wav", { type: 'audio/wav' });
                    const formData = new FormData();
                    formData.append("file", audioFile);
                    formData.append("model", "whisper-1");

                    try {
                        const response = await fetch("https://api.openai.com/v1/audio/transcriptions", {
                            method: "POST",
                            headers: {
                                "Authorization": `Bearer ${getApiKey()}`
                            },
                            body: formData
                        });
                        if (!response.ok) {
                            const errorText = await response.text();
                            console.error("Whisper API error:", errorText);
                            alert("Error with Whisper API:\n" + errorText);
                            return;
                        }
                        const result = await response.json();
                        const transcription = result.text;
                        // Find the chat input textarea and insert the transcription.
                        const chatInput = document.querySelector("textarea");
                        if (chatInput) {
                            chatInput.value = transcription;
                            // Dispatch an input event if the site relies on it.
                            chatInput.dispatchEvent(new Event('input', { bubbles: true }));
                        } else {
                            alert("Chat input not found!");
                        }
                    } catch (error) {
                        console.error("Error calling Whisper API:", error);
                        alert("Error calling Whisper API: " + error.message);
                    }
                });

                mediaRecorder.start();
            } catch (error) {
                console.error("Error accessing microphone:", error);
                alert("Error accessing microphone: " + error.message);
                sttButton.innerHTML = "🎙️ Start STT";
                isRecording = false;
            }
        } else {
            // Stop recording
            sttButton.innerHTML = "🎙️ Start STT";
            isRecording = false;
            if (mediaRecorder && mediaRecorder.state === "recording") {
                mediaRecorder.stop();
            }
        }
    });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址