// ==UserScript== // @name Copilot Text-to-Speech // @namespace https://copilot.microsoft.com/ // @version 1.0 // @author CHJ85 // @description Adds text-to-speech functionality to Copilot / Bing chat for all web browsers. // @match https://copilot.microsoft.com/chats* // @run-at document-start // @grant none // @downloadURL https://update.greasyfork.icu/scripts/541743/Copilot%20Text-to-Speech.user.js // @updateURL https://update.greasyfork.icu/scripts/541743/Copilot%20Text-to-Speech.meta.js // ==/UserScript== (function () { 'use strict'; const TTS_API = 'https://texttospeech.responsivevoice.org/v1/text:synthesize?lang=en-GB&engine=g1&pitch=0.5&rate=0.5&volume=1&key=kvfbSITh&gender=male&text='; let currentAudio = null; const injectSpeakerButtons = () => { document.querySelectorAll('[data-testid="share-message-button"]').forEach(shareBtn => { if (!shareBtn.parentElement.querySelector('.copilot-tts-button')) { const ttsBtn = document.createElement('button'); ttsBtn.className = 'copilot-tts-button'; ttsBtn.title = 'Listen to message'; ttsBtn.style.marginLeft = '6px'; ttsBtn.style.cursor = 'pointer'; ttsBtn.innerHTML = getSVG('speaker'); ttsBtn.onclick = () => { const messageBlock = shareBtn.closest('div[id*="-content-"]'); const spans = messageBlock?.querySelectorAll('span.font-ligatures-none'); const fullMessage = Array.from(spans).map(span => span.textContent.trim()).join(' '); if (!fullMessage) return; if (currentAudio && !currentAudio.paused) { currentAudio.pause(); ttsBtn.innerHTML = getSVG('speaker'); } else if (currentAudio && currentAudio.paused) { currentAudio.play(); ttsBtn.innerHTML = getSVG('pause'); } else { const audioSrc = `${TTS_API}${encodeURIComponent(fullMessage)}`; console.log("🔊 Playing TTS from:", audioSrc); currentAudio = new Audio(audioSrc); currentAudio.onerror = e => console.error("Audio playback error:", e); currentAudio.onended = () => ttsBtn.innerHTML = getSVG('speaker'); currentAudio.oncanplaythrough = () => { currentAudio.play(); ttsBtn.innerHTML = getSVG('pause'); }; } }; shareBtn.parentElement.appendChild(ttsBtn); } }); }; const getSVG = (type) => { if (type === 'pause') { return ``; } // Speaker icon return ``; }; const startObserving = () => { const observer = new MutationObserver(injectSpeakerButtons); observer.observe(document.body, { childList: true, subtree: true }); injectSpeakerButtons(); // Initial run }; window.addEventListener('DOMContentLoaded', startObserving); })();