Spaces:
Runtime error
Runtime error
<html dir="rtl" lang="fa"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
<meta name="description" content="دستیار هوش مصنوعی با قابلیت چت و پاسخگویی"> | |
<meta name="keywords" content="هوش مصنوعی, چت بات, دستیار هوش مصنوعی"> | |
<title>دستیار هوش مصنوعی</title> | |
<!-- فونت وزیر --> | |
<link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazir-font@v30.1.0/dist/font-face.css" rel="stylesheet"> | |
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> | |
<style> | |
:root { | |
--bg-color: #f0f2f5; | |
--chat-bg: white; | |
--text-color: #333; | |
--border-color: #ddd; | |
--primary-color: #0088cc; | |
--hover-color: #006699; | |
--user-message-bg: #e3f2fd; | |
--bot-message-bg: #f5f5f5; | |
--header-height: 120px; | |
--border-radius: 20px; | |
--brain-size: 90px; | |
--animation-timing: cubic-bezier(0.4, 0, 0.2, 1); | |
--shadow-color: rgba(0, 0, 0, 0.2); | |
--glass-effect: rgba(255, 255, 255, 0.1); | |
} | |
.dark-theme { | |
--bg-color: #1a1a1a; | |
--chat-bg: #2d2d2d; | |
--text-color: #fff; | |
--border-color: #444; | |
--primary-color: #0099ff; | |
--hover-color: #0077cc; | |
--user-message-bg: #1e3a5f; | |
--bot-message-bg: #3d3d3d; | |
--shadow-color: rgba(0, 0, 0, 0.4); | |
--glass-effect: rgba(255, 255, 255, 0.05); | |
} | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: 'Vazir', sans-serif; | |
background: var(--bg-color); | |
color: var(--text-color); | |
min-height: 100vh; | |
margin: 0; | |
padding: 20px; | |
transition: all 0.3s var(--animation-timing); | |
overflow-x: hidden; | |
} | |
.app-container { | |
max-width: 1200px; | |
margin: 0 auto; | |
background: var(--chat-bg); | |
border-radius: var(--border-radius); | |
box-shadow: 0 4px 20px var(--shadow-color); | |
overflow: hidden; | |
position: relative; | |
display: flex; | |
flex-direction: column; | |
min-height: calc(100vh - 40px); | |
} | |
.header { | |
height: var(--header-height); | |
background: linear-gradient(135deg, var(--primary-color), #005580); | |
padding: 20px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
position: relative; | |
border-radius: var(--border-radius) var(--border-radius) 0 0; | |
z-index: 10; | |
box-shadow: 0 2px 10px var(--shadow-color); | |
} | |
.header-content { | |
display: flex; | |
align-items: center; | |
gap: 30px; | |
} | |
.brain-icon { | |
width: var(--brain-size); | |
height: var(--brain-size); | |
filter: drop-shadow(3px 3px 5px rgba(0, 0, 0, 0.3)); | |
animation: brainPulse 3s ease-in-out infinite; | |
} | |
@keyframes brainPulse { | |
0% { transform: scale(1) rotate3d(1, 1, 1, 0deg); } | |
50% { transform: scale(1.1) rotate3d(1, 1, 1, 180deg); } | |
100% { transform: scale(1) rotate3d(1, 1, 1, 360deg); } | |
} | |
.header h1 { | |
color: white; | |
margin: 0; | |
font-size: 2.5rem; | |
font-weight: bold; | |
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); | |
} | |
.chat-container { | |
flex: 1; | |
display: flex; | |
flex-direction: column; | |
padding: 20px; | |
position: relative; | |
overflow: hidden; | |
} | |
.messages { | |
flex: 1; | |
overflow-y: auto; | |
padding: 10px; | |
margin-bottom: 20px; | |
scroll-behavior: smooth; | |
} | |
.message { | |
display: flex; | |
align-items: flex-start; | |
margin: 10px 0; | |
max-width: 80%; | |
animation: messageSlide 0.3s var(--animation-timing); | |
} | |
@keyframes messageSlide { | |
from { opacity: 0; transform: translateY(20px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.message-content { | |
flex: 1; | |
padding: 12px 15px; | |
border-radius: 15px; | |
position: relative; | |
box-shadow: 0 2px 5px var(--shadow-color); | |
} | |
.message-time { | |
font-size: 0.75rem; | |
color: #888; | |
margin: 0 10px; | |
white-space: nowrap; | |
align-self: flex-end; | |
} | |
.user-message { | |
margin-left: auto; | |
flex-direction: row-reverse; | |
} | |
.user-message .message-content { | |
background: var(--user-message-bg); | |
border-bottom-right-radius: 5px; | |
} | |
.bot-message { | |
margin-right: auto; | |
flex-direction: row; | |
} | |
.bot-message .message-content { | |
background: var(--bot-message-bg); | |
border-bottom-left-radius: 5px; | |
} | |
.input-area { | |
background: var(--chat-bg); | |
padding: 20px; | |
border-top: 1px solid var(--border-color); | |
} | |
.input-container { | |
display: grid; | |
grid-template-columns: auto 1fr auto; | |
gap: 15px; | |
align-items: center; | |
} | |
.controls { | |
display: flex; | |
gap: 8px; | |
} | |
.control-button { | |
width: 40px; | |
height: 40px; | |
border: none; | |
border-radius: 50%; | |
background: var(--primary-color); | |
color: white; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
cursor: pointer; | |
transition: all 0.2s var(--animation-timing); | |
box-shadow: 0 2px 5px var(--shadow-color); | |
} | |
.control-button:hover { | |
transform: translateY(-2px); | |
background: var(--hover-color); | |
} | |
.message-input { | |
width: 100%; | |
padding: 12px 20px; | |
border: 2px solid var(--border-color); | |
border-radius: 25px; | |
font-size: 1rem; | |
background: var(--chat-bg); | |
color: var(--text-color); | |
transition: all 0.3s var(--animation-timing); | |
} | |
.message-input:focus { | |
outline: none; | |
border-color: var(--primary-color); | |
box-shadow: 0 0 0 3px rgba(0, 136, 204, 0.2); | |
} | |
.send-button { | |
width: 50px; | |
height: 50px; | |
border: none; | |
border-radius: 50%; | |
background: var(--primary-color); | |
color: white; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
cursor: pointer; | |
transition: all 0.2s var(--animation-timing); | |
box-shadow: 0 2px 5px var(--shadow-color); | |
} | |
.send-button:hover { | |
transform: translateY(-2px); | |
background: var(--hover-color); | |
} | |
.send-icon { | |
width: 0; | |
height: 0; | |
border-left: 12px solid transparent; | |
border-right: 12px solid transparent; | |
border-bottom: 20px solid white; | |
transform: rotate(90deg); | |
} | |
.status-bar { | |
padding: 15px; | |
background: var(--chat-bg); | |
border-top: 1px solid var(--border-color); | |
} | |
.status-info { | |
display: grid; | |
grid-template-columns: repeat(3, 1fr); | |
gap: 15px; | |
text-align: center; | |
} | |
.status-badge { | |
padding: 5px 10px; | |
border-radius: 15px; | |
font-size: 0.8rem; | |
display: inline-block; | |
margin-top: 5px; | |
} | |
.typing-indicator { | |
padding: 10px; | |
color: #666; | |
font-style: italic; | |
display: none; | |
} | |
.typing-indicator.active { | |
display: block; | |
animation: typing 1s infinite; | |
} | |
@keyframes typing { | |
0%, 100% { opacity: 0.5; } | |
50% { opacity: 1; } | |
} | |
@media (max-width: 768px) { | |
body { | |
padding: 10px; | |
} | |
:root { | |
--header-height: 100px; | |
--brain-size: 60px; | |
--border-radius: 15px; | |
} | |
.header h1 { | |
font-size: 1.8rem; | |
} | |
.message { | |
max-width: 90%; | |
} | |
.input-container { | |
grid-template-columns: 1fr auto; | |
} | |
.controls { | |
display: none; | |
} | |
.status-info { | |
grid-template-columns: 1fr; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="app-container"> | |
<header class="header"> | |
<div class="header-content"> | |
<h1>دستیار هوش مصنوعی</h1> | |
<svg class="brain-icon" viewBox="0 0 100 100"> | |
<!-- SVG content here --> | |
<defs> | |
<radialGradient id="glass" cx="50%" cy="50%" r="50%"> | |
<stop offset="0%" stop-color="rgba(255, 255, 255, 0.9)" /> | |
<stop offset="70%" stop-color="rgba(255, 255, 255, 0.3)" /> | |
<stop offset="100%" stop-color="rgba(255, 255, 255, 0.1)" /> | |
</radialGradient> | |
<linearGradient id="brainGradient" x1="0%" y1="0%" x2="100%" y2="100%"> | |
<stop offset="0%" stop-color="#ff9eb3" /> | |
<stop offset="50%" stop-color="#ff85a3" /> | |
<stop offset="100%" stop-color="#ff6293" /> | |
</linearGradient> | |
<filter id="glow"> | |
<feGaussianBlur stdDeviation="2" result="coloredBlur" /> | |
<feMerge> | |
<feMergeNode in="coloredBlur" /> | |
<feMergeNode in="SourceGraphic" /> | |
</feMerge> | |
</filter> | |
</defs> | |
<g class="brain-group" filter="url(#glow)"> | |
<path class="brain-path" | |
d="M30,10 C45,0 60,15 55,35 C65,40 60,55 45,50 C35,60 15,50 20,35 C10,30 15,15 30,10" | |
fill="url(#brainGradient)" | |
stroke="#d4357d" | |
stroke-width="2" /> | |
<path class="brain-detail" | |
d="M25,15 C35,10 45,20 40,30" | |
fill="none" | |
stroke="#d4357d" | |
stroke-width="1.5" /> | |
<path class="brain-detail" | |
d="M35,25 C45,20 50,30 45,40" | |
fill="none" | |
stroke="#d4357d" | |
stroke-width="1.5" /> | |
</g> | |
<circle cx="50" cy="50" r="45" | |
fill="url(#glass)" | |
stroke="#ffffff" | |
stroke-width="2" /> | |
<ellipse cx="35" cy="35" rx="15" ry="10" | |
fill="rgba(255, 255, 255, 0.4)" | |
transform="rotate(-30, 35, 35)" /> | |
</svg> | |
</div> | |
</header> | |
<main class="chat-container"> | |
<div class="messages" id="chat-messages"> | |
<div class="message bot-message"> | |
<div class="message-content"> | |
سلام! من یک دستیار هوش مصنوعی هستم. چطور میتوانم کمکتان کنم؟ | |
</div> | |
<div class="message-time">12:00</div> | |
</div> | |
</div> | |
<div class="input-area"> | |
<div class="input-container"> | |
<div class="controls"> | |
<button class="control-button" onclick="toggleTheme()" id="themeToggle">🌙</button> | |
<button class="control-button" onclick="toggleSound()" id="soundToggle">🔊</button> | |
<button class="control-button" onclick="clearChat()">🗑️</button> | |
<button class="control-button" onclick="downloadChat()">💾</button> | |
</div> | |
<input type="text" | |
class="message-input" | |
id="messageInput" | |
placeholder="پیام خود را بنویسید..." | |
autocomplete="off"> | |
<button class="send-button" id="sendBtn" onclick="sendMessage()"> | |
<div class="send-icon"></div> | |
</button> | |
</div> | |
</div> | |
</main> | |
<footer class="status-bar"> | |
<div class="status-info"> | |
<div> | |
<strong>وضعیت مدل:</strong> | |
<span id="modelStatus" class="status-badge bg-warning">در حال بررسی...</span> | |
</div> | |
<div> | |
<strong>وضعیت تلگرام:</strong> | |
<span id="telegramStatus" class="status-badge bg-warning">در حال بررسی...</span> | |
</div> | |
<div> | |
<strong>زمان سیستم:</strong> | |
<span id="systemTime" class="status-badge bg-info">-</span> | |
</div> | |
</div> | |
</footer> | |
</div> | |
<audio id="notificationSound" src="/static/notification.mp3"></audio> | |
<!-- JavaScript Codes --> | |
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script> | |
<script> | |
// WebSocket Connection | |
let ws = new WebSocket( | |
(window.location.protocol === 'https:' ? 'wss://' : 'ws://') + | |
window.location.host + '/ws' | |
); | |
let soundEnabled = true; | |
// WebSocket Event Handlers | |
ws.onopen = function() { | |
console.log('WebSocket Connected'); | |
updateConnectionStatus('online'); | |
}; | |
ws.onclose = function() { | |
console.log('WebSocket Disconnected'); | |
updateConnectionStatus('offline'); | |
setTimeout(reconnectWebSocket, 3000); | |
}; | |
ws.onerror = function(error) { | |
console.error('WebSocket Error:', error); | |
updateConnectionStatus('error'); | |
}; | |
ws.onmessage = function(event) { | |
const data = JSON.parse(event.data); | |
if (data.type === 'typing') { | |
handleTypingStatus(data.status); | |
} else { | |
addMessage(data.message, 'bot'); | |
} | |
}; | |
// Message Functions | |
function sendMessage() { | |
const input = document.getElementById('messageInput'); | |
const message = input.value.trim(); | |
if (message) { | |
addMessage(message, 'user'); | |
ws.send(JSON.stringify({ | |
type: 'message', | |
message: message | |
})); | |
input.value = ''; | |
handleTypingStatus(true); | |
} | |
} | |
function addMessage(message, type) { | |
const messagesDiv = document.getElementById('chat-messages'); | |
const messageDiv = document.createElement('div'); | |
messageDiv.className = `message ${type}-message`; | |
const contentDiv = document.createElement('div'); | |
contentDiv.className = 'message-content'; | |
contentDiv.textContent = message; | |
const timeDiv = document.createElement('div'); | |
timeDiv.className = 'message-time'; | |
const time = new Date().toLocaleTimeString('fa-IR', { | |
hour: '2-digit', | |
minute: '2-digit' | |
}); | |
timeDiv.textContent = time; | |
messageDiv.appendChild(contentDiv); | |
messageDiv.appendChild(timeDiv); | |
messagesDiv.appendChild(messageDiv); | |
messagesDiv.scrollTop = messagesDiv.scrollHeight; | |
if (type === 'bot' && soundEnabled) { | |
playNotification(); | |
} | |
} | |
// UI Control Functions | |
function toggleTheme() { | |
document.body.classList.toggle('dark-theme'); | |
const themeBtn = document.getElementById('themeToggle'); | |
themeBtn.textContent = document.body.classList.contains('dark-theme') ? '☀️' : '🌙'; | |
localStorage.setItem('theme', document.body.classList.contains('dark-theme') ? 'dark' : 'light'); | |
} | |
function toggleSound() { | |
soundEnabled = !soundEnabled; | |
const soundBtn = document.getElementById('soundToggle'); | |
soundBtn.textContent = soundEnabled ? '🔊' : '🔇'; | |
localStorage.setItem('soundEnabled', soundEnabled); | |
} | |
function playNotification() { | |
const audio = document.getElementById('notificationSound'); | |
audio.currentTime = 0; | |
audio.play().catch(err => console.error('Error playing sound:', err)); | |
} | |
function clearChat() { | |
if (confirm('آیا مطمئن هستید که میخواهید تاریخچه چت را پاک کنید؟')) { | |
const messagesDiv = document.getElementById('chat-messages'); | |
messagesDiv.innerHTML = ''; | |
addMessage('سلام! من یک دستیار هوش مصنوعی هستم. چطور میتوانم کمکتان کنم؟', 'bot'); | |
} | |
} | |
function downloadChat() { | |
const messages = document.getElementById('chat-messages'); | |
const text = Array.from(messages.children) | |
.map(msg => { | |
const content = msg.querySelector('.message-content').textContent; | |
const time = msg.querySelector('.message-time').textContent; | |
const type = msg.classList.contains('user-message') ? 'شما' : 'ربات'; | |
return `[${time}] ${type}: ${content}`; | |
}) | |
.join('\n'); | |
const blob = new Blob([text], { type: 'text/plain;charset=utf-8' }); | |
const url = URL.createObjectURL(blob); | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = `chat-${new Date().toLocaleDateString('fa-IR')}.txt`; | |
document.body.appendChild(a); | |
a.click(); | |
document.body.removeChild(a); | |
URL.revokeObjectURL(url); | |
} | |
// Status Management | |
function updateConnectionStatus(status) { | |
const modelStatus = document.getElementById('modelStatus'); | |
const statusText = { | |
'online': 'آنلاین', | |
'offline': 'آفلاین', | |
'error': 'خطا در اتصال' | |
}[status]; | |
const statusClass = { | |
'online': 'bg-success', | |
'offline': 'bg-danger', | |
'error': 'bg-warning' | |
}[status]; | |
modelStatus.textContent = statusText; | |
modelStatus.className = `status-badge ${statusClass}`; | |
} | |
// Typing Indicator | |
let typingTimer; | |
function handleTypingStatus(isTyping) { | |
if (isTyping) { | |
clearTimeout(typingTimer); | |
ws.send(JSON.stringify({ type: 'typing', status: true })); | |
typingTimer = setTimeout(() => { | |
ws.send(JSON.stringify({ type: 'typing', status: false })); | |
}, 1000); | |
} | |
} | |
// Event Listeners | |
document.addEventListener('DOMContentLoaded', function() { | |
// Load saved settings | |
if (localStorage.getItem('theme') === 'dark') { | |
document.body.classList.add('dark-theme'); | |
document.getElementById('themeToggle').textContent = '☀️'; | |
} | |
soundEnabled = localStorage.getItem('soundEnabled') !== 'false'; | |
document.getElementById('soundToggle').textContent = soundEnabled ? '🔊' : '🔇'; | |
// Input event listeners | |
const messageInput = document.getElementById('messageInput'); | |
messageInput.addEventListener('keypress', function(e) { | |
if (e.key === 'Enter' && !e.shiftKey) { | |
e.preventDefault(); | |
sendMessage(); | |
} | |
}); | |
messageInput.addEventListener('input', function() { | |
handleTypingStatus(true); | |
}); | |
// System time update | |
setInterval(() => { | |
const systemTime = document.getElementById('systemTime'); | |
systemTime.textContent = new Date().toLocaleTimeString('fa-IR'); | |
}, 1000); | |
}); | |
// Connection Management | |
function reconnectWebSocket() { | |
ws = new WebSocket( | |
(window.location.protocol === 'https:' ? 'wss://' : 'ws://') + | |
window.location.host + '/ws' | |
); | |
} | |
window.addEventListener('online', function() { | |
reconnectWebSocket(); | |
updateConnectionStatus('online'); | |
}); | |
window.addEventListener('offline', function() { | |
updateConnectionStatus('offline'); | |
}); | |
</script> | |
</body> | |
</html> | |