PI - no hands talk :)

Talk to PI on pi.ai with speech recognition. Works even when the browser is not active window, so you can talk to PI on background and do your thing. Messages are sended after a couple of seconds of non-talking automatically, time can be adjusted by the options at the beginning of the script. Recognition is not that great and a bit junkie, but it still nice mod for now :) I don't know why big companies still dont support this pure speech interface. I guess we not that far.

目前为 2023-09-19 提交的版本。查看 最新版本

// ==UserScript==
// @name         PI - no hands talk :)
// @namespace    http://tampermonkey.net/
// @version      0.1.1
// @description  Talk to PI on pi.ai with speech recognition. Works even when the browser is not active window, so you can talk to PI on background and do your thing. Messages are sended after a couple of seconds of non-talking automatically, time can be adjusted by the options at the beginning of the script. Recognition is not that great and a bit junkie, but it still nice mod for now :) I don't know why big companies still dont support this pure speech interface. I guess we not that far.
// @author       Guki
// @match        https://pi.ai/talk
// @icon         https://www.google.com/s2/favicons?sz=64&domain=pi.ai
// @grant        none
// @license MIT
// ==/UserScript==
(function() {
    'use strict';

    // if you have a job for a person who interested in ML, AI agents, LLMs
    // please contact me, my email: [email protected]

    // options
    const sendDelay = 3500 // time in ms after last registered spoken word before sending a message
    const startActive = false // activate the script on start
    const checkRecognition = 1000 // time in ms of how often to check recognition status to restart it if its become inactive

    let SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
    let recognition = new SpeechRecognition();
    recognition.interimResults = true;

    let recognitionActive = false;
    if (startActive) {
        recognitionActive = true
    }
    recognition.onstart = function() {
        recognitionActive = true;
    };
    recognition.onend = function() {
        recognitionActive = false;
        if (scriptActive) {
            setTimeout(() => {
                // rarely might be active by the interval
                if (!recognitionActive) {
                    recognition.start()
                }
            }, 5)
        }
    };

    let scriptActive = false
    if (startActive) {
        scriptActive = true
        recognition.start();
    }


    // script toggle button
    const button = document.createElement('button');
    button.style.position = 'fixed';
    button.style.top = '120px';
    button.style.left = '24px';
    button.style.borderRadius = '50%';
    button.style.width = '42px';
    button.style.height = '42px';
    button.style.zIndex = '9999';
    button.style.color = 'white';
    button.style.opacity = '0.8';
    if (startActive) {
        button.style.background = '#e63946';
    } else {
        button.style.background = '#a8dadc';
    }
    button.style.paddingLeft = "5px";
    button.style.transition = "all 200ms"
    button.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-8 h-8"><path d="M8.25 4.5a3.75 3.75 0 117.5 0v8.25a3.75 3.75 0 11-7.5 0V4.5z" /><path d="M6 10.5a.75.75 0 01.75.75v1.5a5.25 5.25 0 1010.5 0v-1.5a.75.75 0 011.5 0v1.5a6.751 6.751 0 01-6 6.709v2.291h3a.75.75 0 010 1.5h-7.5a.75.75 0 010-1.5h3v-2.291a6.751 6.751 0 01-6-6.709v-1.5A.75.75 0 016 10.5z" /></svg>'
    button.onclick = function() {
        if (scriptActive) {
            scriptActive = false
            button.style.background = '#a8dadc';
        } else {
            scriptActive = true
            button.style.background = '#e63946';
        }
        if (!scriptActive && recognitionActive) {
            recognition.stop();
        } else if (scriptActive && !recognitionActive) {
            recognition.start();
        }
    };
    document.body.appendChild(button);

    // infinite recognition restart when script is active
    setInterval(function() {
        if (scriptActive && !recognitionActive) {
            recognition.start();
        }
    }, checkRecognition);

    function debounce(func, wait, immediate) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };

    let completeTranscript = ''
    function cleanCompleteTranscript() {
        completeTranscript = ''
    }

    function pressEnter() {
        var textarea = document.querySelector('.block.w-full.resize-none.overflow-y-hidden.whitespace-pre-wrap.bg-transparent');
        var enterEvent = new KeyboardEvent('keydown', {
            key: 'Enter',
            bubbles: true,
            cancelable: true
        });
        textarea.dispatchEvent(enterEvent);
        cleanCompleteTranscript()
    }
    const debouncedPressEnter = debounce(pressEnter, sendDelay);

    let transcript = ""
    recognition.onresult = function(event) {
        let textarea = document.querySelector('.block.w-full.resize-none.overflow-y-hidden.whitespace-pre-wrap.bg-transparent');
        if(!event.results[0].isFinal) {
            // only visual
            transcript = event.results[0][0].transcript
            textarea.value = completeTranscript + ' ' + transcript;
        } else {
            completeTranscript += ' ' + transcript // take transcript from previous iteration, without punctiation

            // value is not registered correctly without this workaround
            textarea.value = ''
            Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set.call(textarea, textarea.value + completeTranscript);
            const inputEvent = new Event('input', { bubbles: true });
            textarea.dispatchEvent(inputEvent);
        }
        debouncedPressEnter();
    };
})();

QingJ © 2025

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