BotVeraAI / script.js
TheVera's picture
Update script.js
610d0de verified
Raw
History Blame Contribute Delete
23.1 kB
// Load data from JSON file
// window.speechSynthesis.onvoiceschanged = () => {
// const voices = window.speechSynthesis.getVoices();
// console.log('Available voices:', voices);
// };
document.addEventListener('DOMContentLoaded', function() {
const token = localStorage.getItem('authToken');
const userInfoDiv = document.getElementById('user-info');
const loginLink = document.getElementById('login-link');
const charchaButton = document.getElementById('charchaButton');
if (token) {
// Check token validity with backend
fetch('https://thevera-botveradb.hf.space/validate_token', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => response.json())
.then(data => {
// if (data.success) {
if (data.message === "Token is valid!") {
// Token is valid, extract user's email and display it
const userObject = jwt_decode(token); // Decode the token to get user info
const email = userObject.email; // Assuming the email is stored in the token
const username = email.split('@')[0];
// Display user information
document.getElementById('user-name').innerText = username; // Display first name
userInfoDiv.style.display = 'block'; // Show user info
document.getElementById('logout-button').style.display = 'inline'; // Show logout button
loginLink.style.display = 'none'; // Hide login link
} else {
// Token is invalid, clear invalid token
localStorage.removeItem('authToken');
console.log('Invalid token, user not logged in.');
loginLink.style.display = 'inline'; // Show login link
}
})
.catch(error => {
console.error('Error:', error);
// Handle the error (e.g., network issues)
localStorage.removeItem('authToken'); // Clear token in case of error
loginLink.style.display = 'inline'; // Show login link
});
} else {
// No token found, show login link
console.log('No token found, user not logged in.');
loginLink.style.display = 'inline';
}
// Logout function
const logoutButton = document.getElementById('logout-button');
if (logoutButton) {
logoutButton.onclick = function() {
localStorage.removeItem('authToken'); // Clear token
window.location.reload(); // Refresh page to update UI
};
}
// Function to handle redirection for Sastra Charcha button
function redirectToCharcha() {
if (token) {
// If the token exists, verify it
fetch('https://thevera-botveradb.hf.space/validate_token', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => response.json())
.then(data => {
if (data.message === "Token is valid!") {
// Token is valid, redirect to Sastra Charcha page
window.location.href = 'https://thevera-shastracharcha.static.hf.space';
window.location.href = 'sastra.html';
} else {
// Token is invalid, redirect to registration page
localStorage.removeItem('authToken'); // Clear invalid token
window.location.href = 'login.html';
}
})
.catch(error => {
console.error('Error:', error);
localStorage.removeItem('authToken'); // Clear token on error
window.location.href = 'login.html'; // Redirect to registration if there's an error
});
} else {
// No token, redirect to registration page
window.location.href = 'login.html';
}
}
// Attach the redirect function to the button
if (charchaButton) {
charchaButton.addEventListener('click', redirectToCharcha);
}
});
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(registration => {
console.log('ServiceWorker registered: ', registration);
})
.catch(error => {
console.log('ServiceWorker registration failed: ', error);
});
});
}
// Check if the browser supports the Web Speech API
if (!('webkitSpeechRecognition' in window)) {
alert("Your browser doesn't support speech recognition. Please use Google Chrome.");
} else {
// Initialize speech recognition
const recognition = new webkitSpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = 'en-IN'; // Default language (can be 'hi-IN' for Hindi)
const micButton = document.getElementById('micButton'); // Microphone icon
const userInput = document.getElementById('userInput'); // Text input field
const chatBody = document.getElementById('chatBody'); // Chat history container
// Voice Input Button Click Event
micButton.addEventListener('click', () => {
if (micButton.classList.contains('active')) {
recognition.stop(); // Stop if already active
} else {
recognition.start(); // Start listening
}
});
// When speech recognition starts
recognition.onstart = () => {
micButton.classList.add('active'); // Change mic color to green
};
// When speech recognition ends
recognition.onend = () => {
micButton.classList.remove('active'); // Reset mic color to red
};
// When speech recognition result is detected
recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
userInput.value = transcript; // Fill the input with transcribed text
sendMessage(); // Call sendMessage after getting the input
};
// Handle errors during speech recognition
recognition.onerror = (event) => {
alert('Speech recognition error: ' + event.error);
micButton.classList.remove('active'); // Reset mic color to red if error occurs
};
}
// Add an event listener to the input field to detect the Enter key
const userInput = document.getElementById('userInput');
userInput.addEventListener('keydown', function(event) {
// Check if the Enter key (key code 13) is pressed
if (event.key === 'Enter') {
event.preventDefault(); // Prevent the default behavior (new line)
sendMessage(); // Call your send message function
}
});
let isSpeakEnabled = true;
function toggleHistory() {
const historyPanel = document.querySelector('.history-panel');
const chatHistoryText = document.querySelector('.chat-history-text');
// Toggle the width of the history panel
if (historyPanel.style.width === '0px') {
historyPanel.style.width = '250px'; // Open the panel
chatHistoryText.style.display = 'block'; // Show "Chat History" text
} else {
historyPanel.style.width = '0px'; // Close the panel
chatHistoryText.style.display = 'none'; // Hide "Chat History" text
}
}
function toggleMenu() {
const menu = document.getElementById("menuOptions");
menu.style.display = menu.style.display === "block" ? "none" : "block";
}
function openSettings() {
document.getElementById("settingsModal").style.display = "block";
document.getElementById("menuOptions").style.display = "none"; // Close menu when settings open
}
function closeSettings() {
document.getElementById("settingsModal").style.display = "none";
}
function toggleSpeak() {
const speakToggle = document.getElementById("speakToggle");
isSpeakEnabled = speakToggle.checked;
console.log(isSpeakEnabled ? "Speech enabled" : "Speech disabled");
}
// Close the modal when clicking outside of it
window.onclick = function(event) {
const modal = document.getElementById("settingsModal");
if (event.target === modal) {
closeSettings();
}
}
function startNewChat() {
// Select the chat body element
const chatBody = document.getElementById('chatBody');
// Remove all chat messages
chatBody.innerHTML = ''; // This clears all existing messages
// Optionally, add the default welcome message again
const welcomeMessage = document.createElement('div');
welcomeMessage.classList.add('bot-message');
welcomeMessage.textContent = "Hello! How can I assist you today?";
chatBody.appendChild(welcomeMessage);
// Scroll to the top after resetting chat
chatBody.scrollTop = chatBody.scrollHeight;
}
function remove_special_chars(text) {
// Allow only specified characters
const allow = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 :,.?!";
// Replace <br> with a space for better readability
text = text.replace(/<br>/g, " ");
let newtxt = "";
// Iterate through the text and include only allowed characters
for (let char of text) {
if (allow.includes(char)) {
newtxt += char;
}
}
return newtxt;
}
// Helper function to speak text and execute a callback when done
function speakTextSequential(text, language, callback) {
if (!isSpeakEnabled) {
console.log('Speech is disabled.');
if (callback) callback(); // Call callback even if speech is disabled
return; // Exit if speech is disabled
}
// Clean the text of HTML tags
text = text.replace(/<\/?[^>]+(>|$)/g, "");
if ('speechSynthesis' in window) {
// Cancel any ongoing speech synthesis to avoid overlap
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
// Set the language based on the input
switch (language) {
case 'hi':
utterance.lang = 'hi-IN'; // Hindi
break;
case 'en':
default:
utterance.lang = 'en-IN'; // English (India)
break;
}
// Set voice preferences to find a male voice
const voices = window.speechSynthesis.getVoices();
const maleVoice = voices.find(voice => {
return (voice.name.includes('Google') || voice.name.includes('Indian')) && voice.name.toLowerCase().includes('male');
});
if (maleVoice) {
utterance.voice = maleVoice;
} else {
// If no specific male voice found, fallback to any voice
const defaultVoice = voices.find(voice => voice.name.includes('Google') || voice.name.includes('Indian'));
if (defaultVoice) {
utterance.voice = defaultVoice;
}
}
// Set pitch, rate, and volume for a more natural sound
utterance.pitch = 1; // Adjust pitch for a male voice
utterance.rate = 1; // Normal speed
utterance.volume = 1; // Maximum volume
// Insert pauses for a more teaching-like effect
const pauseDuration = 500; // Pause duration in milliseconds
const splitText = text.split(/(?<=[.!?])\s+/); // Split text by punctuation and following spaces
const speakingOrder = splitText.map(part => part.trim()).filter(part => part); // Clean up split results and filter out empty strings
// Function to speak the segments sequentially
const speakSegment = (index) => {
if (index < speakingOrder.length) {
const segment = speakingOrder[index];
if (segment) {
utterance.text = segment; // Set the text to speak
speechSynthesis.speak(utterance);
utterance.onend = () => speakSegment(index + 1); // Speak next segment after the current one ends
} else {
speakSegment(index + 1); // Move to the next index if the segment is empty
}
} else {
if (callback) {
callback(); // Call the callback when done
}
}
};
// Start speaking the first segment
speakSegment(0);
// Handle potential speech synthesis errors
utterance.onerror = function(event) {
console.error('SpeechSynthesisUtterance error:', event.error);
if (callback) {
callback(); // Call callback even if there's an error
}
};
} else {
alert('Text-to-Speech not supported in your browser.');
if (callback) {
callback(); // Call callback if TTS is not supported
}
}
}
// Format bot response and handle categories
function formatBotResponse(rawResponse) {
// Replace newlines with actual <br> for line breaks
let rawResponseList = rawResponse.split('\n');
let category = rawResponseList.length > 0 ? rawResponseList[0] : "General"; // Use ternary for category assignment
let onlyResponse = rawResponseList.slice(1).join('\n'); // Use slice to skip the first element
let formattedResponse = onlyResponse
.replace(/\n/g, '<br>') // Replace newline with line breaks
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') // Bold text between ** **
.replace(/\*(.*?)\*/g, '<em>$1</em>') // Italic text between * *
.replace(/(\d+\.\s.*?)(?=\n|$)/g, '<li>$1</li>'); // Convert numbered lists
// Wrap list items in <ul> tags
formattedResponse = formattedResponse.replace(/(<li>.*?<\/li>)+/g, '<ul>$&</ul>');
return { formattedResponse, category }; // Return an object for clarity
}
function sendMessage() {
const message = userInput.value.trim();
if (message !== "") {
appendMessage(message, 'user-message');
userInput.value = '';
// Disable the mic button to prevent multiple submissions during processing
micButton.disabled = true;
let username = null;
let isRegisteredUser = false;
// Try to get the user info from the token stored in local storage
try {
const token = localStorage.getItem('authToken');
if (token) {
const userObject = jwt_decode(token); // Decode the JWT token to get user info
username = userObject.email; // Extract username (email) from the decoded token
isRegisteredUser = !!username; // Check if user is registered (truthy if email exists)
}
} catch (error) {
console.error("Error decoding token or no token found:", error);
}
// Prepare the request data to send to the backend
const requestData = {
user_data: message,
bot_data: null // Placeholder for the bot's response
};
// Send the user message to the backend for processing
fetch('https://thevera-backendvera.hf.space/generate_result', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: message })
})
.then(response => {
if (!response.ok) {
throw new Error("Failed to generate result");
}
return response.json();
})
.then(data => {
let botResponses = data.response;
const language = data.language || 'en';
const { formattedResponse: botResponse, category } = formatBotResponse(botResponses); // Destructure correctly
appendMessage(category, 'bot-message'); // Show the category
// Regular expression to match all mantras or sloks within [[ ]]
const mantraRegex = /\[\[(.*?)\]\]/g;
let lastIndex = 0; // Keep track of the last index processed
let blocks = []; // Array to hold the sections of text and mantras
let match;
// Loop over all mantra matches and build blocks array
while ((match = mantraRegex.exec(botResponse)) !== null) {
const mantraText = match[1].trim(); // Extract the mantra/slok content
const mantraStartIndex = match.index; // Start of mantra/slok
// Extract the text before this mantra and add it to blocks
const beforeMantraText = botResponse.substring(lastIndex, mantraStartIndex).trim();
if (beforeMantraText) {
blocks.push({ type: 'text', content: beforeMantraText });
}
// Add the mantra/slok to the blocks
blocks.push({ type: 'mantra', content: mantraText });
// Update the last index to move past the current mantra/slok
lastIndex = mantraRegex.lastIndex;
}
// Add any remaining text after the last mantra/slok
const afterLastMantraText = botResponse.substring(lastIndex).trim();
if (afterLastMantraText) {
blocks.push({ type: 'text', content: afterLastMantraText });
}
// Process the blocks sequentially
processSequentially(blocks, language);
// Store the conversation in MongoDB
requestData.bot_data = botResponse; // Add the bot's response before saving
// Determine backend URL for storing the chat
const baseURL = 'https://thevera-botveradb.hf.space';
const storeChatURL = isRegisteredUser
? `${baseURL}/store_registered_chat/${username}`
: `${baseURL}/store_anonymous_chat`;
// Store chat data
return fetch(storeChatURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestData)
});
})
.then(response => {
if (!response.ok) {
throw new Error("Failed to store chat data");
}
return response.json();
})
.then(data => {
console.log(data.message); // Success message for storing chat
micButton.disabled = false; // Re-enable mic button after response
})
.catch(error => {
alert('Failed to connect to the backend. Please try again later.');
console.error('Error:', error);
micButton.disabled = false; // Re-enable mic button after error
});
}
}
// Manta Block Function
// Recursive function to process each block sequentially
function processSequentially(blocks, language, index = 0) {
if (index >= blocks.length) {
return; // Exit if all blocks are processed
}
const block = blocks[index];
if (block.type === 'text') {
// Show and speak the text block
appendMessage(block.content, 'bot-message');
speakTextSequential(block.content, language, () => {
// Move to the next block once speaking is done
processSequentially(blocks, language, index + 1);
});
} else if (block.type === 'mantra') {
// Show and speak the mantra block
displayMantra(block.content);
speakMantraSequential(block.content, 1, () => {
// Move to the next block once the mantra is spoken
processSequentially(blocks, language, index + 1);
});
}
}
// Function to display the mantra block
function displayMantra(mantraText) {
const mantraBlock = document.createElement('div');
mantraBlock.className = 'mantra-block'; // Add class for styling
mantraBlock.innerHTML = `<pre id="mantraText">${mantraText}</pre>
<label for="repeat-count">Repeat mantra:</label>
<div id="repeat-control">
<button onclick="decreaseCount()">-</button>
<input type="number" id="repeat-count" value="1" min="1" max="100" />
<button onclick="increaseCount()">+</button>
</div>
<div class="play-mantra-button-container">
<button class="play-mantra-button" onclick="repeatMantra()">&#9654;</button> <!-- Play button icon -->
</div>`;
document.getElementById('chatBody').appendChild(mantraBlock); // Append to chat body
}
// Function to hide the mantra block when there are no mantras
function hideMantraBlock() {
let mantraBlock = document.getElementById('mantra-block');
mantraBlock.style.display = 'none';
}
// Function to decrease the repeat count
function decreaseCount() {
let repeatCountInput = document.getElementById('repeat-count');
let currentValue = parseInt(repeatCountInput.value);
if (currentValue > 1) {
repeatCountInput.value = currentValue - 1;
}
}
// Function to increase the repeat count
function increaseCount() {
let repeatCountInput = document.getElementById('repeat-count');
let currentValue = parseInt(repeatCountInput.value);
repeatCountInput.value = currentValue + 1;
}
// Function to speak the mantra with repeat functionality
function speakMantraSequential(mantraText, repeatCount, callback) {
const utterance = new SpeechSynthesisUtterance(mantraText);
utterance.lang = 'sa-IN'; // Sanskrit
utterance.rate = 0.8; // Slow speech for mantras
utterance.pitch = 1.0; // Natural pitch
utterance.onend = function() {
if (repeatCount > 1) {
repeatCount--;
speechSynthesis.speak(utterance); // Speak again if more repetitions are needed
} else if (callback) {
callback(); // Call the callback when done
}
};
// Handle potential speech synthesis errors
utterance.onerror = function(event) {
console.error('SpeechSynthesisUtterance error:', event.error);
if (callback) {
callback(); // Call callback even if there's an error
}
};
// Cancel any ongoing speech synthesis before speaking
window.speechSynthesis.cancel();
speechSynthesis.speak(utterance);
}
// Function to handle mantra repetition based on user input
function repeatMantra() {
let mantraText = document.getElementById('mantraText').textContent;
let repeatCount = parseInt(document.getElementById('repeat-count').value);
if (repeatCount < 1) {
alert("Repeat count must be at least 1");
return;
}
// Speak the mantra the number of times entered by the user
speakMantraSequential(mantraText, repeatCount, 1);
}
// Mantra Black Function extends
let isFirstMessage = true; // Flag to track if it's the first message
// Function to speak the mantra
function appendMessage(text, className) {
// Remove the default message when the user sends the first message
if (isFirstMessage) {
const welcomeMessage = document.querySelector('.welcome-message');
if (welcomeMessage) {
welcomeMessage.remove();
}
isFirstMessage = false;
}
const messageElement = document.createElement('div');
messageElement.classList.add('user-message', className);
// Use innerHTML to allow formatted content (e.g., bold, italic, line breaks)
messageElement.innerHTML = text;
chatBody.appendChild(messageElement);
chatBody.scrollTop = chatBody.scrollHeight; // Auto scroll to the latest message
}
// Donate Logic
// Toggle menu visibility
// function toggleMenu() {
// const menuOptions = document.getElementById('menuOptions');
// menuOptions.style.display = menuOptions.style.display === 'none' ? 'block' : 'none';
// }
// Show donate modal
function showDonateModal() {
document.getElementById('donateModal').style.display = 'block';
}
// Close donate modal
function closeDonateModal() {
document.getElementById('donateModal').style.display = 'none';
}
// Copy UPI ID to clipboard
function copyUpiId() {
const upiId = document.getElementById("upiId").innerText;
navigator.clipboard.writeText(upiId).then(() => {
alert("UPI ID copied to clipboard");
}).catch(err => {
console.error('Could not copy text: ', err);
});
}