讓你可以透過語音輸入要問 ChatGPT 的問題 (支援中文、英文、日文、韓文)
// ==UserScript==
// @name ChatGPT 語音輸入介面 (支援中/英/日/韓語言)
// @version 1.0
// @description 讓你可以透過語音輸入要問 ChatGPT 的問題 (支援中文、英文、日文、韓文)
// @license MIT
// @homepage https://wayneblog.ga/
// @homepageURL https://wayneblog.ga/
// @website https://www.facebook.com/wayne.blog.ga
// @source https://github.com/wjdesign/TampermonkeyScripts/raw/main/ChatGPTWithVoiceInput.user.js
// @namespace https://github.com/wjdesign/TampermonkeyScripts/raw/main/ChatGPTWithVoiceInput.user.js
// @match *://chat.openai.com/chat
// @author Wayne
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
let sti = setInterval(() => {
if (document.activeElement.tagName === 'TEXTAREA' && document.activeElement.nextSibling.tagName === 'BUTTON') {
var vi = new VoiceInputHelper(document.activeElement, document.activeElement.nextSibling);
vi.Start();
document.addEventListener('keydown', (ev) => {
if (ev.altKey && (ev.key === 'S' || ev.key === 'T') && !/^(?:input|select|textarea|button)$/i.test(ev.target.nodeName)) {
alert('你是不是不小心按到了 CAPSLOCK 鍵?');
return;
}
if (ev.altKey && ev.key === 's' /* && !/^(?:input|select|textarea|button)$/i.test(ev.target.nodeName) */) {
if (!vi.IsStarted) {
vi.Restart = true;
vi.Start();
}
}
if (ev.altKey && ev.key === 't' /* && !/^(?:input|select|textarea|button)$/i.test(ev.target.nodeName) */) {
if (vi.IsStarted) {
vi.Restart = false;
vi.Stop();
}
}
});
clearInterval(sti);
}
}, 100);
class VoiceInputHelper {
IsStarted = false;
parts = [];
Restart = true;
Lang = 'cmn-Hant-TW';
constructor(textarea, button, lang) {
// console.log(textarea, button);
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_Web_Speech_API
const SpeechRecognition = window.SpeechRecognition || webkitSpeechRecognition;
// const SpeechGrammarList = window.SpeechGrammarList || webkitSpeechGrammarList;
// const SpeechRecognitionEvent = window.SpeechRecognitionEvent || webkitSpeechRecognitionEvent;
this.recognition = new SpeechRecognition();
// show.value = "您好,接下來的對話請都使用繁體中文回應。";
// button.click();
this.recognition.continuous = false;
this.recognition.interimResults = true;
this.setLang();
this.recognition.onstart = () => {
console.log('開始進行 SpeechRecognition 語音辨識');
this.IsStarted = true;
};
this.recognition.onend = () => {
console.log('停止 SpeechRecognition 語音辨識!');
this.IsStarted = false;
setTimeout(() => {
if (this.Restart) {
this.recognition.start();
}
}, 60);
};
this.recognition.onresult = (event) => {
// console.log('語音事件: ', event);
var i = event.resultIndex;
let results = event.results[i];
console.log('results.length', results.length);
let transcript = results[0].transcript; // 理論上只會有一個結果
console.log('語音輸入: ' + transcript, 'isFinal: ', results.isFinal);
if (this.parts.length == 0) {
this.parts[0] = transcript;
} else {
this.parts[this.parts.length - 1] = transcript;
}
textarea.value = this.parts.join('') + '...';
textarea.dispatchEvent(new Event('input', {bubbles:true}));
if (results.isFinal) {
console.log('Final Result: ', results);
switch (this.parts[this.parts.length - 1]) {
case '送出':
case 'Submit':
case 'submit':
case '跑起來':
case '去吧':
case 'enter':
case 'Enter':
case 'Run':
case 'run':
case 'Go':
case 'go':
this.parts.pop();
if (this.parts.length > 0) {
textarea.value = this.parts.join('');
textarea.dispatchEvent(new Event('input', {bubbles:true}));
button.click();
this.parts = [];
}
break;
case '清空':
case '淨空':
case 'clear':
this.parts = [];
break;
case '刪除':
case '刪除上一句':
this.parts.pop();
this.parts.pop();
break;
case '逗號':
case '逗點':
case '都好':
this.parts[this.parts.length - 1] = ',';
break;
case '句號':
case '句點':
this.parts[this.parts.length - 1] = '。';
break;
case '問號':
this.parts[this.parts.length - 1] = '?';
break;
case '斷行':
this.parts[this.parts.length - 1] = '\r\n';
break;
case '重置':
case 'リセット': // Risetto
case '초기화': // chogihwa
case 'reset':
this.setLang('cmn-Hant-TW');
this.parts = [];
break;
case '切換至中文模式':
case '切換至中文':
case 'switch to Chinese mode':
this.setLang('cmn-Hant-TW');
this.parts[this.parts.length - 1] = '';
break;
case '切換至英文模式':
case '切換至英文':
console.log('切換至英文模式');
this.setLang('en-US');
this.parts[this.parts.length - 1] = '';
break;
case '切換至日文模式':
case '切換至日文':
console.log('切換至日文模式');
this.setLang('ja-JP');
this.parts[this.parts.length - 1] = '';
break;
case '切換至韓文模式':
case '切換至韓文':
console.log('切換至韓文模式');
this.setLang('ko-KR');
this.parts[this.parts.length - 1] = '';
break;
case '關閉語音辨識':
case '關閉語音':
this.Stop();
break;
default:
this.parts[this.parts.length - 1] = this.parts[this.parts.length - 1].replace(/\.\.\.$/g, '');
if (this.parts[this.parts.length - 1].split('').pop() === '嗎') {
this.parts[this.parts.length - 1] += '?';
}
break;
}
this.parts = [...this.parts, ''];
textarea.value = this.parts.join('');
textarea.dispatchEvent(new Event('input', {bubbles:true}));
}
};
}
setLang(lang) {
// https://stackoverflow.com/a/68742566/910074
if (lang) {
this.Lang = lang;
}
this.recognition.lang = this.Lang;
}
Start() {
this.recognition.start();
}
Stop() {
this.restart = false;
this.recognition.stop();
}
}
})();