0% found this document useful (0 votes)
109 views13 pages

308 Boutique Chat Bot Codes

The document contains HTML and JavaScript code for a virtual assistant chat widget for The 308 Boutique. It includes styles for the chat interface, user input handling, and bot responses, as well as functionality for collecting user information and interacting with a backend service. The chat widget allows users to ask questions, make purchases, and receive assistance in a conversational format.

Uploaded by

nobelc150
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
109 views13 pages

308 Boutique Chat Bot Codes

The document contains HTML and JavaScript code for a virtual assistant chat widget for The 308 Boutique. It includes styles for the chat interface, user input handling, and bot responses, as well as functionality for collecting user information and interacting with a backend service. The chat widget allows users to ask questions, make purchases, and receive assistance in a conversational format.

Uploaded by

nobelc150
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

frontend html code

<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" href="data:," />

<meta charset="UTF-8" />


<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>The 308 Boutique Virtual Assistant</title>
<style>
/* Body and General Styles */
body {
font-family: "Roboto", sans-serif;
background-color: #eef2f3;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}

/* Chat Widget Styles */


#chat-widget {
position: fixed;
bottom: 20px;
right: 20px;
width: 60px;
height: 60px;
background-color: #4267b2;
border-radius: 50%;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
transition: transform 0.3s ease, background-color 0.3s ease;
color: #fff;
font-size: 1.5rem;
}

#chat-widget:hover {
transform: scale(1.1);
background-color: #375a99;
}

/* Chatbox Container */
#chatbox {
position: fixed;
bottom: 90px;
right: 20px;
width: 400px;
height: 600px;
background: linear-gradient(
to bottom,
#a8edea,
#fed6e3
); /* Chatbox background */
border-radius: 15px;
box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.15);
display: none;
flex-direction: column;
overflow: hidden;
}

/* Chat Log Styles */


#chat-log {
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 15px;
}

/* User Input Section */


#user-input {
display: flex;
padding: 10px;
background-color: #e9ecef;
border-top: 1px solid #dee2e6;
}

/* Message Styles */
.message {
display: flex;
align-items: flex-end;
}

.message.bot {
justify-content: flex-start;
}

.message.user {
justify-content: flex-end;
}

.message-content {
max-width: 70%;
padding: 12px 16px;
border-radius: 20px;
font-size: 0.9rem;
position: relative;
word-wrap: break-word;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}

.message.bot .message-content {
background: linear-gradient(to right, #eff9ff, #cde7f6);
color: #004d73;
border: none;
}

.message.user .message-content {
background: linear-gradient(to right, #d4fc79, #96e6a1);
color: #083d1a;
border: none;
}
.message-avatar img {
display: inline-block;
width: 30px;
height: 30px;
border-radius: 50%;
object-fit: cover;
}

.message-avatar {
display: inline-block;
vertical-align: top;
margin-right: 8px;
}
.message-avatar img {
border: 2px solid blue; /* Debugging: Red border around images */
}

.timestamp {
font-size: 0.75rem;
color: #6c757d;
margin-top: 5px;
}

/* Typing Indicator */
.typing-indicator {
display: flex;
align-items: center;
}

.typing-indicator span {
width: 8px;
height: 8px;
background-color: #4267b2;
border-radius: 50%;
margin: 0 2px;
animation: blink 1s infinite;
}

.typing-indicator span:nth-child(2) {
animation-delay: 0.2s;
}

.typing-indicator span:nth-child(3) {
animation-delay: 0.4s;
}

@keyframes blink {
0%,
100% {
opacity: 0.2;
}
50% {
opacity: 1;
}
}
@media screen and (max-width: 768px) {
#chatbox {
width: 90%;
height: 70%;
bottom: 10px;
right: 5%;
}

#user-input {
flex-direction: column;
}

#user-message {
margin-bottom: 10px;
}
}
body {
font-size: 1rem; /* Base font size */
}

.message-content {
font-size: 0.9rem;
}
button {
padding: 12px 16px; /* Larger touch target */
font-size: 1rem;
}

/* Buttons */
button {
padding: 10px 16px;
margin: 5px;
border: none;
border-radius: 8px;
background-color: #4267b2;
color: #fff;
font-size: 0.9rem;
cursor: pointer;
transition: background-color 0.3s, box-shadow 0.3s;
}

button:hover {
background-color: #375a99;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
}
button:hover {
transform: translateY(-3px);
background-color: #2d4373;
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.3);
}

button:active {
transform: scale(0.98);
}

/* Input Field */
input[type="text"] {
flex: 1;
padding: 12px;
border: 1px solid #d4d9de;
border-radius: 20px;
font-size: 1rem;
margin-right: 10px;
}

input[type="text"]:focus {
outline: none;
border-color: #4267b2;
}
</style>
</head>
<body>
<div id="chat-widget" onclick="toggleChat()">💬</div>
<div id="chatbox">
<div id="chat-log"></div>
<div id="user-input">
<input type="text" id="user-message" placeholder="Type a message..." />
<button onclick="sendMessage()">Send</button>
</div>
</div>

<script>
let userInfo = {
name: "",
phone: "",
address: "",
product: "",
};
let step = 0;

function sendMessage() {
const input = document.getElementById("user-message").value.trim();
if (!input) return;

simulateUserResponse(input); // Display the user's message


document.getElementById("user-message").value = ""; // Clear the input
field

if (step === 0) {
botMessageWithDelay(
"Hello! Welcome to The 308 Boutique's virtual assistant. How can I
assist you today?",
[
{
label: "I want to make a purchase",
onClick: startLeadCollection,
},
{ label: "I have some queries", onClick: queryResponse },
]
);
step = 1; // Update the step to avoid repeating the intro
} else if (step === 1) {
sendToGPT(input); // Handle GPT queries
} else if (step >= 2 && step <= 5) {
captureLeadInfo(input); // Collect user details in steps
} else {
botMessageWithDelay(
"I'm not sure how to handle that. Please try again."
);
}
}
async function sendToGPT(message) {
const typingIndicator = displayTypingIndicator(); // Show typing indicator
try {
const response = await fetch("http://127.0.0.1:5000/query", {
method: "POST",
headers: {
"Content-Type": "application/json", // Indicate JSON payload
},
body: JSON.stringify({ message }), // Send the message to the server
credentials: "include", // Include cookies or session data
});

if (!response.ok) {
throw new Error(`Server responded with status: ${response.status}`);
}

const data = await response.json();


removeTypingIndicator(typingIndicator); // Remove typing indicator

if (data && data.response) {


simulateBotResponse(data.response); // Handle valid response
} else {
simulateBotResponse("Sorry, I couldn't process your request."); //
Handle missing response
}
} catch (error) {
console.error("Error:", error); // Log error for debugging
removeTypingIndicator(typingIndicator); // Remove typing indicator
simulateBotResponse("An error occurred. Please try again later."); //
Show fallback error message
}
}

document
.getElementById("user-message")
.addEventListener("keypress", function (e) {
if (e.key === "Enter") {
sendMessage();
}
});

function displayProductCarousel(products) {
let carouselHTML =
'<div class="carousel" style="display: flex; overflow-x: auto;">';

products.forEach((p) => {
carouselHTML += `
<div class="carousel-item" style="min-width: 150px; margin-right: 15px;
text-align: center;">
<h4>${p.name}</h4>
<p>${p.price}</p>
<button onclick="selectProduct('${p.name}')">Select</button>
</div>
`;
});

carouselHTML += "</div>";
simulateBotResponse(carouselHTML);
}
function selectProduct(productName) {
simulateUserResponse(`I am interested in ${productName}`);
sendToGPT(productName);
}

function toggleChat() {
const chatbox = document.getElementById("chatbox");
if (!chatbox.style.display || chatbox.style.display === "none") {
chatbox.style.display = "flex"; // Open the chatbox
if (step === 0) {
botMessage(
"Hello! Welcome to The 308 Boutique's virtual assistant. How can I
help you find the perfect outfit today?",
[
{
label: "I want to make a purchase",
onClick: startLeadCollection,
},
{ label: "I have some queries", onClick: queryResponse },
]
);
step++;
}
} else {
chatbox.style.display = "none"; // Close the chatbox
}
}

function botMessage(message, buttons = []) {


const chatLog = document.getElementById("chat-log");
const botMsg = document.createElement("div");
botMsg.className = "message bot";

botMsg.innerHTML = `
<div class="message-avatar">
<img src="robot-avatar.png" alt="Bot Avatar" />
</div>
<div class="message-content">
${message}
<div class="timestamp">${new Date().toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}</div>
<div class="button-container">
${buttons
.map(
(button, index) =>
`<button class="action-button" data-index="${index}">$
{button.label}</button>`
)
.join("")}
</div>
</div>
`;

chatLog.appendChild(botMsg);
chatLog.scrollTop = chatLog.scrollHeight;
const buttonElements = botMsg.querySelectorAll(".action-button");
buttonElements.forEach((btn, index) => {
btn.addEventListener("click", () => {
disableAllButtons();
simulateUserResponse(buttons[index].label);
buttons[index].onClick();
});
});
}

function disableAllButtons() {
document.querySelectorAll(".action-button").forEach((button) => {
button.disabled = true;
button.style.opacity = 0.5;
button.style.pointerEvents = "none";
});
}

function simulateBotResponse(responseText) {
const chatLog = document.getElementById("chat-log");
const botMsg = document.createElement("div");
botMsg.className = "message bot";
botMsg.innerHTML = `
<div class="message-avatar">
<img src="robot-avatar.png" alt="Bot Avatar" />
</div>
<div class="message-content">
${responseText}
<div class="timestamp">${new Date().toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}</div>
</div>
`;
chatLog.appendChild(botMsg);
chatLog.scrollTop = chatLog.scrollHeight;
}

function botMessageWithDelay(message, buttons = [], delay = 1000) {


const typingIndicator = displayTypingIndicator();
setTimeout(() => {
removeTypingIndicator(typingIndicator);
botMessage(message, buttons);
}, delay);
}

function displayTypingIndicator() {
const chatLog = document.getElementById("chat-log");
const typingIndicator = document.createElement("div");
typingIndicator.className = "typing-indicator message bot";
typingIndicator.innerHTML = `
<div class="message-avatar">
<img src="robot-avatar.png" alt="Bot Avatar" />
</div>
<div class="message-content">
<div class="typing-indicator">
<span></span><span></span><span></span>
</div>
</div>
`;
chatLog.appendChild(typingIndicator);
chatLog.scrollTop = chatLog.scrollHeight;
return typingIndicator;
}

function removeTypingIndicator(typingIndicator) {
if (typingIndicator && typingIndicator.parentNode) {
typingIndicator.parentNode.removeChild(typingIndicator);
}
}

function startLeadCollection() {
step = 2;
botMessageWithDelay(
"Great! We'll walk you through a few steps to ensure your purchase is
secure."
);
botMessageWithDelay("What is your name?");
}

function queryResponse() {
step = 1;
botMessageWithDelay("Please go on. What would you like to ask?");
}

function captureLeadInfo(input) {
if (step === 2) {
userInfo.name = input;
step = 3;
botMessageWithDelay(
`Thank you, ${userInfo.name}. What is your phone number?`
);
} else if (step === 3) {
userInfo.phone = input;
step = 4;
botMessageWithDelay("Got it! Where are you located?");
} else if (step === 4) {
userInfo.address = input;
step = 5;
botMessageWithDelay("What product are you interested in?");
} else if (step === 5) {
userInfo.product = input;
botMessageWithDelay(
`Thanks ${userInfo.name}. Here is the information you provided:<br>
Name: ${userInfo.name}<br>
Phone number: ${userInfo.phone}<br>
Location and address: ${userInfo.address}<br>
Product: ${userInfo.product}<br>
Are the above information correct?`,
[
{ label: "Yes it is", onClick: confirmDetails },
{
label: "No there is a mistake",
onClick: restartLeadCollection,
},
]
);
}
}

function confirmDetails() {
botMessageWithDelay(
`Great, your data was sent to our company. <a href="tel:+13082702206"
style="color:blue; text-decoration:underline;">Click here for a direct call with
our customer service representative.</a>`
);
}

function restartLeadCollection() {
step = 2;
botMessageWithDelay("Let's start over. What is your name?");
}

function simulateUserResponse(responseText) {
const chatLog = document.getElementById("chat-log");
const userMsg = document.createElement("div");
userMsg.className = "message user";
userMsg.innerHTML = `
<div class="message-avatar">
<img src="humanAvatar.png" alt="User Avatar" />
</div>
<div class="message-content">
${responseText}
<div class="timestamp">${new Date().toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}</div>
</div>
`;
chatLog.appendChild(userMsg);
chatLog.scrollTop = chatLog.scrollHeight;
}
</script>
</body>
</html>

backend python code


import logging
from pathlib import Path
from fuzzywuzzy import process
from flask import Flask, request, jsonify, session
from flask_cors import CORS
from openai import OpenAI
import random
import os

# Initialize Flask app


app = Flask(__name__)
app.secret_key = os.getenv("FLASK_SECRET_KEY", "supersecretkey") # Use environment
variable for security
CORS(app, resources={r"/*": {"origins": "http://127.0.0.1:5500"}},
supports_credentials=True)
# Allow all origins

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %
(message)s')
# Load OpenAI API Key from environment variable
api_key = "sk-proj-XG59K-99ghLLWTnFy0n-s7SoZJXsGsFx5lGaJAJ_SYK6yjpekpZg0gYRaU_EPYL-
dWuG41JB32T3BlbkFJFqz0vf7I9M-
r8zPDpxIyuTBGuixbREsjLmuI2SPawYOlcC70h1P0utN1ifFyaCNx4lME9DwWcA"

if not api_key:
raise EnvironmentError("OpenAI API key is not set in environment variables.")
client = OpenAI(api_key=api_key)

# System prompt
SYSTEM_PROMPT = (
"You are a helpful assistant for The 308 Boutique. Always provide responses
based on the provided product list "
"and database file (database.txt).\n"
"- Extract relevant keywords from the user's query to identify product
categories or items of interest.\n"
"- Recommend between 6 to 10 items with their prices that match the query. If
no products match, offer alternatives "
"or ask clarifying questions to help the user.\n"
"- Respond accurately to any questions related to the database.txt file,
prioritizing accuracy over assumptions.\n"
"- Provide helpful and context-aware suggestions for products or store policies
based on user input."
)

# File paths
PRODUCTS_FILE = Path("D:/chatbots/308boutique/products.txt")
DATABASE_FILE = Path("D:/chatbots/308boutique/database.txt")

# Global products list


products = []

def load_products(file_path):
"""Load products from the given file."""
try:
with file_path.open("r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if line and ":" in line: # Expecting format 'Product Name: $Price'
try:
product_name, price = line.split(":", 1)
products.append({"name": product_name.strip(), "price":
price.strip()})
except ValueError:
logging.warning(f"Skipping invalid product entry: {line}")
logging.info(f"Loaded {len(products)} products.")
except FileNotFoundError:
logging.error(f"Error: '{file_path}' not found.")

def find_products(query):
"""Find products matching the query."""
if not products:
return []

# General product matching


matches = process.extract(query, [p['name'] for p in products], limit=20)
relevant_products = [
{"name": match, "price": next((p['price'] for p in products if p['name'] ==
match), "Unknown")}
for match, score in matches if score > 50 # Match threshold
]

# Seasonal keyword matching


seasonal_keywords = []
if "winter" in query.lower():
seasonal_keywords = ["sweater", "coat", "jacket", "scarf", "beanie",
"boots", "thermal", "knit"]
elif "summer" in query.lower():
seasonal_keywords = ["dress", "tank top", "shorts", "sandals", "hat",
"sunglasses", "linen", "lightweight"]

if seasonal_keywords:
keyword_matches = [
{"name": p['name'], "price": p['price']}
for p in products if any(keyword in p['name'].lower() for keyword in
seasonal_keywords)
]
relevant_products.extend(keyword_matches)

# Deduplicate and prioritize


unique_products = {product['name']: product for product in relevant_products}
relevant_products = list(unique_products.values())

# Ensure a minimum of 6 items


if len(relevant_products) < 6:
additional_products = [p for p in products if p not in relevant_products]
random.shuffle(additional_products)
relevant_products.extend(additional_products[:6 - len(relevant_products)])

# Limit to a maximum of 10 items


random.shuffle(relevant_products)
return relevant_products[:10]

def get_database_response(file_path, query=None):


"""Fetch relevant database information based on the query."""
try:
with file_path.open("r", encoding="utf-8") as f:
lines = f.readlines()
if query:
if "contact" in query.lower() or "phone" in query.lower():
for line in lines:
if "contact" in line.lower() or "phone" in line.lower():
return line.strip()
matching_lines = [line for line in lines if query.lower() in
line.lower()]
if matching_lines:
return "\n".join(matching_lines).strip()
else:
return "Sorry, no relevant information found in the database."
return "".join(lines).strip()
except FileNotFoundError:
logging.error(f"Error: '{file_path}' not found.")
return "The database is currently unavailable."

@app.route('/query', methods=['POST'])
def query():
"""Handle queries from the frontend and return GPT-generated responses."""
try:
data = request.json
user_input = data.get('message', '').strip()

matched_products = find_products(user_input)
product_details = (
"\n".join([f"{p['name']}: {p['price']}" for p in matched_products])
if matched_products else "No matching products found."
)
database_content = get_database_response(DATABASE_FILE, user_input)

prompt = f"{SYSTEM_PROMPT}\nUser Query: {user_input}"


response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "system", "content": SYSTEM_PROMPT}, {"role":
"user", "content": prompt}],
max_tokens=250,
temperature=0.2
)

return jsonify({"response": response.choices[0].message.content.strip()})

except Exception as e:
logging.error(f"Error processing query: {e}")
return jsonify({"response": "An error occurred while processing the
query."}), 500

if __name__ == '__main__':
load_products(PRODUCTS_FILE)
app.run(debug=True, port=5000)

You might also like