I am new on IOS Systems and also new to Apple devices an ran into a big Problem. I created a script that can be implemented in User Websites which is kindo of a voice agent. On non IOS Systems such as Android or a simple Laptop under Windows it runs as expected. The user aks something and gets an answer voice driven. Under IOS it needs some kind of interaction which is done by an modal which opens but after the first message is given out to the user the script stops working. So no second interaction can be done what do i miss or what do i wrong this is the JS script part.
(function() {
document.addEventListener('DOMContentLoaded', function() {
// Funktion zum Setzen eines Cookies
function setCookie(name, value, days) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
const expires = "expires=" + date.toUTCString();
document.cookie = name + "=" + value + ";" + expires + ";path=/";
}
// Funktion zum Abrufen eines Cookies
function getCookie(name) {
const nameEQ = name + "=";
const ca = document.cookie.split(';');
for(let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(nameEQ) == 0) {
return c.substring(nameEQ.length, c.length);
}
}
return null;
}
// Überprüfen, ob der Tooltip bereits geschlossen wurde
const tooltipClosed = getCookie('tooltipClosed');
// Parameter aus der URL der eingebetteten JavaScript-Datei auslesen
const scriptTag = document.currentScript || document.querySelector('script[src*="micButton.js"]');
if (!scriptTag) {
console.error('Das aktuelle Skript konnte nicht gefunden werden.');
return;
}
const scriptUrl = new URL(scriptTag.src);
const userId = scriptUrl.searchParams.get('user_id');
// Überprüfen, ob die erforderlichen Parameter vorhanden sind
if (!userId) {
console.error('Fehlende Parameter: user_id');
return;
}
// CSS hinzufügen
const style = document.createElement('style');
style.textContent = `
.mic-checkbox {
display: none;
}
.mic-checkbox:checked + .mic-button {
transform: rotateY(180deg);
}
.mic-checkbox:checked + .mic-button span {
}
.button-container {
perspective: 500px;
position: fixed;
bottom: 10px;
right: 10px;
opacity: 0;
transform: translateY(10px);
animation: fadeInButton 0.5s forwards;
}
.mic-button {
display: flex;
align-items: center;
justify-content: center;
height: 100px;
width: 100px;
border-radius: 100%;
transition: transform 0.4s;
border: 2px solid #333;
transform-style: preserve-3d;
position: relative;
font-size: 58px;
}
.button-message, .mic, .mic-back {
backface-visibility: hidden;
}
.button-message {
position: absolute;
width: 100px;
height: 100px;
z-index: 2;
transform: rotateY(0deg);
pointer-events: none;
left: 0;
top: 0;
display: flex;
align-items: center;
justify-content: center;
}
.button-message img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
}
.mic-back {
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
transform: rotateY(180deg);
pointer-events: none;
top: 0;
left: 0;
background-image: url('data:image/png;base64,'); /* Hier dein Base64-Bild */
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 50%;
}
.mic-button-loader {
position: absolute;
height: 102px;
width: 100px;
background-color: transparent;
transform: rotateY(180deg);
top: -31px;
left: -50px;
}
.mic-checkbox:checked + .mic-button > .mic > .mic-button-loader {
border-top: 3px solid #AA1111;
border-radius: 100%;
animation: borderLoader 1.3s 0.2s ease-in-out infinite;
}
.mic {
position: relative;
top: -11px;
height: 20px;
width: 0;
border-radius: 10px;
transform: rotateY(180deg);
}
.mic:after, .mic:before, .mic-base {
position: absolute;
}
@keyframes borderLoader {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
.tooltip-speaksdata {
position: fixed;
bottom: 25px;
right: 110px;
background-color: #333;
color: #fff;
padding: 10px;
border-radius: 5px;
font-family: 'Montserrat', sans-serif;
font-size: 14px;
display: flex;
align-items: center;
z-index: 1000;
width: 170px;
opacity: 0;
animation: fadeIn 0.5s forwards;
transition: opacity 0.5s, transform 0.5s;
}
.tooltip-speaksdata span {
margin-right: 10px;
}
.tooltip-speaksdata .close-tooltip {
cursor: pointer;
background-color: #555;
border: none;
color: #fff;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
position:absolute;
right:-10px;
top:-10px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeOut {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-10px);
}
}
@keyframes fadeInButton {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
`;
document.head.appendChild(style);
// Base64-Icons für verschiedene Zustände
const loadingIconBase64 = 'data:image/png;base64,'; // Lade-Icon
const successIconBase64 = 'data:image/png;base64,'; // Erfolgs-Icon
const errorIconBase64 = 'data:image/png;base64,'; // Fehler-Icon
const defaultIconBase64 = 'data:image/png;base64,'; // Standard-Icon
const buttonContainer = document.createElement('div');
buttonContainer.classList.add('button-container');
buttonContainer.innerHTML = `
<input type="checkbox" id="micButton" class="mic-checkbox">
<label for="micButton" class="mic-button">
<div class="mic">
<div class="mic-button-loader"></div>
</div>
<div class="button-message">
<img src="${defaultIconBase64}" id="buttonIcon" alt="Speaksdata KI Assistent">
</div>
<div class="mic-back">
</div>
</label>
`;
document.body.appendChild(buttonContainer);
const micButtonCheckbox = document.getElementById('micButton');
const buttonIcon = document.getElementById('buttonIcon');
// Tooltip-Element erstellen
if (!tooltipClosed) {
setTimeout(() => {
const tooltip = document.createElement('div');
tooltip.classList.add('tooltip-speaksdata');
tooltip.innerHTML = `
<span>Ich bin der Speaksdata Assistent!<br>Ich helfe Ihnen bei Fragen!</span>
<button class="close-tooltip">×</button>
`;
document.body.appendChild(tooltip);
// Tooltip schließen
document.querySelector('.close-tooltip').addEventListener('click', function() {
tooltip.style.animation = 'fadeOut 0.5s forwards';
setCookie('tooltipClosed', 'true', 365);
});
}, 5000); // Tooltip erscheint nach 5 Sekunden
}
// Hilfsfunktionen zum Verwalten von Elementen
function updateOrCreateElement(id, tag, parent, content = '') {
let element = document.getElementById(id);
if (!element) {
element = document.createElement(tag);
element.id = id;
element.style.display = 'none';
parent.appendChild(element);
}
element.textContent = content;
return element;
}
// Funktionen zur Feature-Erkennung für iOS und Safari
function isIos() {
return /iPhone|iPad|iPod/.test(navigator.userAgent);
}
function isSafari() {
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
// Funktion zur Anzeige eines Modals zur Bestätigung der Audiowiedergabe
function showAudioConsentModal(audioUrl) {
const modal = document.createElement('div');
modal.style.position = 'fixed';
modal.style.top = '50%';
modal.style.left = '50%';
modal.style.transform = 'translate(-50%, -50%)';
modal.style.padding = '20px';
modal.style.backgroundColor = 'white';
modal.style.border = '1px solid black';
modal.style.zIndex = '10000';
modal.innerHTML = `
<p>Möchten Sie die Antwort anhören?</p>
<button id="playAudioButton">Ja</button>
<button id="cancelButton">Nein</button>
`;
document.body.appendChild(modal);
document.getElementById('playAudioButton').addEventListener('click', () => {
const audio = new Audio(audioUrl);
audio.play().catch(error => {
console.error('Audio playback failed:', error);
});
modal.remove();
});
document.getElementById('cancelButton').addEventListener('click', () => {
modal.remove();
});
}
micButtonCheckbox.addEventListener('change', () => {
if (micButtonCheckbox.checked) {
const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
recognition.lang = 'de-DE';
const resetButtonIcon = () => {
buttonIcon.src = defaultIconBase64;
};
recognition.onresult = async (event) => {
if (event.results.length > 0) {
const text = event.results[0][0].transcript;
// Verwende updateOrCreateElement, um das Gesprochen-Text-Element zu aktualisieren oder zu erstellen
const spokenTextElement = updateOrCreateElement('spokenText', 'p', document.body, `Gesprochen: ${text}`);
recognition.stop();
micButtonCheckbox.checked = false;
// Lade-Icon sofort anzeigen, nachdem die Spracherkennung beendet ist
buttonIcon.src = loadingIconBase64;
try {
const response = await fetch('https://www.speaksdata.com/query.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: text,
user_id: userId
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
// Verwende updateOrCreateElement, um das Antwort-Text-Element zu aktualisieren oder zu erstellen
const responseTextElement = updateOrCreateElement('responseText', 'p', document.body, `Antwort: ${result.answer}`);
buttonIcon.src = successIconBase64;
if (result.audio_base64) {
const audioBlob = new Blob([Uint8Array.from(atob(result.audio_base64), c => c.charCodeAt(0))], { type: 'audio/mp3' });
const audioUrl = URL.createObjectURL(audioBlob);
if (isIos() || isSafari()) {
showAudioConsentModal(audioUrl);
} else {
const audio = new Audio(audioUrl);
audio.play().catch(error => {
console.error('Audio playback failed:', error);
});
audio.onended = () => {
URL.revokeObjectURL(audioUrl);
resetButtonIcon(); // Zurücksetzen auf das Standard-Icon nach Audiowiedergabe
};
}
} else {
const utterance = new SpeechSynthesisUtterance(result.answer);
window.speechSynthesis.speak(utterance);
utterance.onend = resetButtonIcon; // Zurücksetzen auf das Standard-Icon nach Text-to-Speech
}
} catch (error) {
// Verwende updateOrCreateElement, um das Fehler-Text-Element zu aktualisieren oder zu erstellen
const responseTextElement = updateOrCreateElement('responseText', 'p', document.body, `Fehler bei der Anfrage: ${error.message}`);
buttonIcon.src = errorIconBase64;
const utterance = new SpeechSynthesisUtterance("Ein Fehler ist aufgetreten");
utterance.onerror = (e) => {
console.error('TTS Error:', e);
alert("Ein Fehler ist aufgetreten");
};
window.speechSynthesis.speak(utterance);
// Icon auf Standard-Icon zurücksetzen, nachdem die Fehlermeldung abgeschlossen ist
utterance.onend = resetButtonIcon;
}
} else {
micButtonCheckbox.checked = false;
recognition.stop();
}
};
recognition.onerror = (event) => {
const responseTextElement = updateOrCreateElement('responseText', 'p', document.body, `Spracherkennungsfehler: ${event.error}`);
micButtonCheckbox.checked = false;
recognition.stop();
buttonIcon.src = errorIconBase64;
const utterance = new SpeechSynthesisUtterance("Ein Fehler ist aufgetreten");
utterance.onerror = (e) => {
console.error('TTS Error:', e);
alert("Ein Fehler ist aufgetreten");
};
window.speechSynthesis.speak(utterance);
// Icon auf Standard-Icon zurücksetzen, nachdem die Fehlermeldung abgeschlossen ist
utterance.onend = resetButtonIcon;
};
recognition.onspeechend = () => {
recognition.stop();
micButtonCheckbox.checked = false;
buttonIcon.src = loadingIconBase64; // Lade-Icon wird sofort nach Ende der Spracherkennung angezeigt
};
recognition.start();
}
});
});
})();
I do not have a mac or Iphone myself and recordnized it by a friend, hope someone can help me with this problem.
If a website will be needed i will also provide this to find the failure and set this script up.
Regards and thanks, Phil
I wanted to get an voice driven answer from the script everytime i asked i tried several userinteractions but nothing worked.