diff --git a/api/chat.php b/api/chat.php new file mode 100644 index 0000000..dbe026c --- /dev/null +++ b/api/chat.php @@ -0,0 +1,64 @@ + "I didn't catch that. Could you repeat?"]); + exit; +} + +try { + // 1. Fetch Knowledge Base (FAQs) + $stmt = db()->query("SELECT keywords, answer FROM faqs"); + $faqs = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $knowledgeBase = "Here is the knowledge base for this website:\n\n"; + foreach ($faqs as $faq) { + $knowledgeBase .= "Q: " . $faq['keywords'] . "\nA: " . $faq['answer'] . "\n---\n"; + } + + // 2. Construct Prompt for AI + $systemPrompt = "You are a helpful, friendly AI assistant for this website. " . + "Use the provided Knowledge Base to answer user questions accurately. " . + "If the answer is found in the Knowledge Base, rephrase it naturally. " . + "If the answer is NOT in the Knowledge Base, use your general knowledge to help, " . + "but politely mention that you don't have specific information about that if it seems like a site-specific question. " . + "Keep answers concise and professional.\n\n" . + $knowledgeBase; + + // 3. Call AI API + $response = LocalAIApi::createResponse([ + 'model' => 'gpt-4o-mini', + 'input' => [ + ['role' => 'system', 'content' => $systemPrompt], + ['role' => 'user', 'content' => $message], + ] + ]); + + if (!empty($response['success'])) { + $aiReply = LocalAIApi::extractText($response); + + // 4. Save to Database + try { + $stmt = db()->prepare("INSERT INTO messages (user_message, ai_response) VALUES (?, ?)"); + $stmt->execute([$message, $aiReply]); + } catch (Exception $e) { + error_log("DB Save Error: " . $e->getMessage()); + // Continue even if save fails, so the user still gets a reply + } + + echo json_encode(['reply' => $aiReply]); + } else { + // Fallback if AI fails + error_log("AI Error: " . ($response['error'] ?? 'Unknown')); + echo json_encode(['reply' => "I'm having trouble connecting to my brain right now. Please try again later."]); + } + +} catch (Exception $e) { + error_log("Chat Error: " . $e->getMessage()); + echo json_encode(['reply' => "An internal error occurred."]); +} diff --git a/api/telegram_webhook.php b/api/telegram_webhook.php new file mode 100644 index 0000000..fa4899c --- /dev/null +++ b/api/telegram_webhook.php @@ -0,0 +1,91 @@ +query("SELECT setting_value FROM settings WHERE setting_key = 'telegram_token'"); +$token = $stmt->fetchColumn(); + +if (!$token) { + error_log("Telegram Error: No bot token found in settings."); + exit; +} + +function sendTelegramMessage($chatId, $text, $token) { + $url = "https://api.telegram.org/bot$token/sendMessage"; + $data = [ + 'chat_id' => $chatId, + 'text' => $text, + 'parse_mode' => 'Markdown' + ]; + + $options = [ + 'http' => [ + 'header' => "Content-type: application/x-www-form-urlencoded\r\n", + 'method' => 'POST', + 'content' => http_build_query($data), + ], + ]; + $context = stream_context_create($options); + return file_get_contents($url, false, $context); +} + +// Process with AI (Similar logic to api/chat.php) +try { + // 1. Fetch Knowledge Base + $stmt = db()->query("SELECT keywords, answer FROM faqs"); + $faqs = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $knowledgeBase = "Here is the knowledge base for this website:\n\n"; + foreach ($faqs as $faq) { + $knowledgeBase .= "Q: " . $faq['keywords'] . "\nA: " . $faq['answer'] . "\n---\n"; + } + + $systemPrompt = "You are a helpful AI assistant integrated with Telegram. " . + "Use the provided Knowledge Base to answer user questions. " . + "Keep answers concise for mobile reading. Use Markdown for formatting.\n\n" . + $knowledgeBase; + + // 2. Call AI + $response = LocalAIApi::createResponse([ + 'model' => 'gpt-4o-mini', + 'input' => [ + ['role' => 'system', 'content' => $systemPrompt], + ['role' => 'user', 'content' => $text], + ] + ]); + + if (!empty($response['success'])) { + $aiReply = LocalAIApi::extractText($response); + + // 3. Save History + try { + $stmt = db()->prepare("INSERT INTO messages (user_message, ai_response) VALUES (?, ?)"); + $stmt->execute(["[Telegram] " . $text, $aiReply]); + } catch (Exception $e) {} + + // 4. Send back to Telegram + sendTelegramMessage($chatId, $aiReply, $token); + } else { + sendTelegramMessage($chatId, "I'm sorry, I encountered an error processing your request.", $token); + } + +} catch (Exception $e) { + error_log("Telegram Webhook Error: " . $e->getMessage()); +} diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..789132e --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,403 @@ +body { + background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); + background-size: 400% 400%; + animation: gradient 15s ease infinite; + color: #212529; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + font-size: 14px; + margin: 0; + min-height: 100vh; +} + +.main-wrapper { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + width: 100%; + padding: 20px; + box-sizing: border-box; + position: relative; + z-index: 1; +} + +@keyframes gradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +.chat-container { + width: 100%; + max-width: 600px; + background: rgba(255, 255, 255, 0.85); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 20px; + display: flex; + flex-direction: column; + height: 85vh; + box-shadow: 0 20px 40px rgba(0,0,0,0.2); + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); + overflow: hidden; +} + +.chat-header { + padding: 1.5rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); + background: rgba(255, 255, 255, 0.5); + font-weight: 700; + font-size: 1.1rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.chat-messages { + flex: 1; + overflow-y: auto; + padding: 1.5rem; + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +/* Custom Scrollbar */ +::-webkit-scrollbar { + width: 6px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.3); + border-radius: 10px; +} + +::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.5); +} + +.message { + max-width: 85%; + padding: 0.85rem 1.1rem; + border-radius: 16px; + line-height: 1.5; + font-size: 0.95rem; + box-shadow: 0 4px 15px rgba(0,0,0,0.05); + animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px) scale(0.95); } + to { opacity: 1; transform: translateY(0) scale(1); } +} + +.message.visitor { + align-self: flex-end; + background: linear-gradient(135deg, #212529 0%, #343a40 100%); + color: #fff; + border-bottom-right-radius: 4px; +} + +.message.bot { + align-self: flex-start; + background: #ffffff; + color: #212529; + border-bottom-left-radius: 4px; +} + +.chat-input-area { + padding: 1.25rem; + background: rgba(255, 255, 255, 0.5); + border-top: 1px solid rgba(0, 0, 0, 0.05); +} + +.chat-input-area form { + display: flex; + gap: 0.75rem; +} + +.chat-input-area input { + flex: 1; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 12px; + padding: 0.75rem 1rem; + outline: none; + background: rgba(255, 255, 255, 0.9); + transition: all 0.3s ease; +} + +.chat-input-area input:focus { + border-color: #23a6d5; + box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2); +} + +.chat-input-area button { + background: #212529; + color: #fff; + border: none; + padding: 0.75rem 1.5rem; + border-radius: 12px; + cursor: pointer; + font-weight: 600; + transition: all 0.3s ease; +} + +.chat-input-area button:hover { + background: #000; + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(0,0,0,0.2); +} + +/* Background Animations */ +.bg-animations { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + overflow: hidden; + pointer-events: none; +} + +.blob { + position: absolute; + width: 500px; + height: 500px; + background: rgba(255, 255, 255, 0.2); + border-radius: 50%; + filter: blur(80px); + animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1); +} + +.blob-1 { + top: -10%; + left: -10%; + background: rgba(238, 119, 82, 0.4); +} + +.blob-2 { + bottom: -10%; + right: -10%; + background: rgba(35, 166, 213, 0.4); + animation-delay: -7s; + width: 600px; + height: 600px; +} + +.blob-3 { + top: 40%; + left: 30%; + background: rgba(231, 60, 126, 0.3); + animation-delay: -14s; + width: 450px; + height: 450px; +} + +@keyframes move { + 0% { transform: translate(0, 0) rotate(0deg) scale(1); } + 33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); } + 66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); } + 100% { transform: translate(0, 0) rotate(360deg) scale(1); } +} + +.header-link { + font-size: 14px; + color: #fff; + text-decoration: none; + background: rgba(0, 0, 0, 0.2); + padding: 0.5rem 1rem; + border-radius: 8px; + transition: all 0.3s ease; +} + +.header-link:hover { + background: rgba(0, 0, 0, 0.4); + text-decoration: none; +} + +/* Admin Styles */ +.admin-container { + max-width: 900px; + margin: 3rem auto; + padding: 2.5rem; + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border-radius: 24px; + box-shadow: 0 20px 50px rgba(0,0,0,0.15); + border: 1px solid rgba(255, 255, 255, 0.4); + position: relative; + z-index: 1; +} + +.admin-container h1 { + margin-top: 0; + color: #212529; + font-weight: 800; +} + +.table { + width: 100%; + border-collapse: separate; + border-spacing: 0 8px; + margin-top: 1.5rem; +} + +.table th { + background: transparent; + border: none; + padding: 1rem; + color: #6c757d; + font-weight: 600; + text-transform: uppercase; + font-size: 0.75rem; + letter-spacing: 1px; +} + +.table td { + background: #fff; + padding: 1rem; + border: none; +} + +.table tr td:first-child { border-radius: 12px 0 0 12px; } +.table tr td:last-child { border-radius: 0 12px 12px 0; } + +.form-group { + margin-bottom: 1.25rem; +} + +.form-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; + font-size: 0.9rem; +} + +.form-control { + width: 100%; + padding: 0.75rem 1rem; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 12px; + background: #fff; + transition: all 0.3s ease; + box-sizing: border-box; +} + +.form-control:focus { + outline: none; + border-color: #23a6d5; + box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1); +} + +.header-container { + display: flex; + justify-content: space-between; + align-items: center; +} + +.header-links { + display: flex; + gap: 1rem; +} + +.admin-card { + background: rgba(255, 255, 255, 0.6); + padding: 2rem; + border-radius: 20px; + border: 1px solid rgba(255, 255, 255, 0.5); + margin-bottom: 2.5rem; + box-shadow: 0 10px 30px rgba(0,0,0,0.05); +} + +.admin-card h3 { + margin-top: 0; + margin-bottom: 1.5rem; + font-weight: 700; +} + +.btn-delete { + background: #dc3545; + color: white; + border: none; + padding: 0.25rem 0.5rem; + border-radius: 4px; + cursor: pointer; +} + +.btn-add { + background: #212529; + color: white; + border: none; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; + margin-top: 1rem; +} + +.btn-save { + background: #0088cc; + color: white; + border: none; + padding: 0.8rem 1.5rem; + border-radius: 12px; + cursor: pointer; + font-weight: 600; + width: 100%; + transition: all 0.3s ease; +} + +.webhook-url { + font-size: 0.85em; + color: #555; + margin-top: 0.5rem; +} + +.history-table-container { + overflow-x: auto; + background: rgba(255, 255, 255, 0.4); + padding: 1rem; + border-radius: 12px; + border: 1px solid rgba(255, 255, 255, 0.3); +} + +.history-table { + width: 100%; +} + +.history-table-time { + width: 15%; + white-space: nowrap; + font-size: 0.85em; + color: #555; +} + +.history-table-user { + width: 35%; + background: rgba(255, 255, 255, 0.3); + border-radius: 8px; + padding: 8px; +} + +.history-table-ai { + width: 50%; + background: rgba(255, 255, 255, 0.5); + border-radius: 8px; + padding: 8px; +} + +.no-messages { + text-align: center; + color: #777; +} \ No newline at end of file diff --git a/assets/pasted-20260313-215047-bb9b4d8a.png b/assets/pasted-20260313-215047-bb9b4d8a.png new file mode 100644 index 0000000..a33e295 Binary files /dev/null and b/assets/pasted-20260313-215047-bb9b4d8a.png differ