const db = require("../config/db"); const axios = require("axios"); const OPENAI_API_KEY = "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"; // 🔹 Add API Key const scopeData = [ { "title": "Greetings | التحيات", "topics": [ { "name": "English Greetings", "details": [ "Hello!", "Hi there!", "Greetings!", "Good morning!", "Good afternoon!", "Good evening!" ] }, { "name": "التحيات العربية", "details": [ "مرحبا!", "أهلا!", "السلام عليكم!", "صباح الخير!", "مساء الخير!" ] } ] }, { "title": "National Development Summit 2025 | قمة التنمية الوطنية 2025", "overview": { "description_en": "The National Development Summit 2025 will be held at the Qatar National Convention Centre (QNCC) in Doha, Qatar, on March 8, 2025. This summit will bring together policymakers, business leaders, and industry experts to discuss economic development, social progress, and sustainability.", "description_ar": "ستُعقد قمة التنمية الوطنية 2025 في مركز قطر الوطني للمؤتمرات (QNCC) في الدوحة، قطر، في 8 مارس 2025. ستجمع هذه القمة صانعي السياسات وقادة الأعمال والخبراء لمناقشة التنمية الاقتصادية والتقدم الاجتماعي والاستدامة." }, "topics": [ { "name": "Event Details | تفاصيل الفعالية", "details": [ "📅 Date: March 8, 2025 | 📅 التاريخ: 8 مارس 2025", "📍 Venue: QNCC, Doha, Qatar | 📍 الموقع: مركز قطر الوطني للمؤتمرات (QNCC)، الدوحة، قطر", "🎤 Speakers: Top government officials, industry leaders | 🎤 المتحدثون: كبار المسؤولين الحكوميين وقادة الصناعة", "📝 Registration: Available online | 📝 التسجيل: متاح عبر الإنترنت", "💡 Key Topics: Economic diversification, sustainability, AI in governance | 💡 المواضيع الرئيسية: تنويع الاقتصاد، الاستدامة، الذكاء الاصطناعي في الحوكمة" ] }, { "name": "Venue Information | معلومات المكان", "details": [ "🚗 Parking: Free and paid parking available | 🚗 مواقف السيارات: متاحة مجانًا ومدفوعة", "🚌 Public Transport: Metro & Bus services to QNCC | 🚌 وسائل النقل: خدمات المترو والحافلات إلى QNCC", "🏨 Accommodation: Hotels nearby | 🏨 الإقامة: فنادق قريبة متاحة", "🍽️ Dining: Cafeterias and restaurants available at the venue | 🍽️ الطعام: توجد كافيتريات ومطاعم داخل المركز" ] } ] }, { "title": "Qatar National Vision 2030 | رؤية قطر الوطنية 2030", "overview": { "description_en": "Qatar National Vision 2030 aims to transform Qatar into an advanced country capable of sustaining its development and ensuring a high standard of living for its people. It consists of four pillars: Human, Social, Economic, and Environmental Development.", "description_ar": "تهدف رؤية قطر الوطنية 2030 إلى تحويل قطر إلى دولة متقدمة قادرة على الحفاظ على تنميتها وضمان مستوى معيشي مرتفع لشعبها. وتتكون من أربعة ركائز: التنمية البشرية، والتنمية الاجتماعية، والتنمية الاقتصادية، والتنمية البيئية." }, "pillars": [ { "name": "Human Development | التنمية البشرية", "focus_areas": [ { "title": "Education | التعليم", "details": [ "A world-class education system that meets international standards.", "Developing high-quality research institutions.", "Accessible lifelong learning programs." ] }, { "title": "Healthcare | الرعاية الصحية", "details": [ "A world-class healthcare system accessible to all citizens.", "Advanced medical research and preventive healthcare programs.", "Integration of AI in medical services." ] } ] }, { "name": "Social Development | التنمية الاجتماعية", "focus_areas": [ { "title": "Culture & Heritage | الثقافة والتراث", "details": [ "Preserving Qatar’s cultural identity and Islamic heritage.", "Encouraging arts, literature, and creative industries." ] }, { "title": "Women Empowerment | تمكين المرأة", "details": [ "Increasing women's participation in political and economic sectors.", "Supporting women entrepreneurs." ] } ] }, { "name": "Economic Development | التنمية الاقتصادية", "focus_areas": [ { "title": "Economic Diversification | تنويع الاقتصاد", "details": [ "Reducing reliance on oil and gas sectors.", "Encouraging foreign direct investment.", "Developing financial services, technology, and tourism sectors." ] }, { "title": "Entrepreneurship | ريادة الأعمال", "details": [ "Providing financial support and incubation centers for startups.", "Strengthening the digital economy and e-commerce." ] } ] }, { "name": "Environmental Development | التنمية البيئية", "focus_areas": [ { "title": "Sustainability | الاستدامة", "details": [ "Increasing renewable energy production.", "Reducing carbon emissions by 25% by 2030.", "Encouraging sustainable urban development." ] } ] } ] }, { "title": "General Questions | الأسئلة العامة", "topics": [ { "name": "Summit Questions | أسئلة عن القمة", "details": [ "How can I register for the National Development Summit?", "Where is the Qatar National Convention Centre located?", "What topics will be discussed at the event?", "Will there be live streaming for the summit?", "What is the dress code for the event?" ] }, { "name": "Travel & Accommodation | السفر والإقامة", "details": [ "What hotels are near QNCC?", "How do I reach QNCC using public transport?", "Are there parking facilities at the venue?", "What are the best places to eat near QNCC?" ] }, { "name": "Qatar National Vision 2030 | رؤية قطر الوطنية 2030", "details": [ "What are the four pillars of Qatar National Vision 2030?", "How is Qatar investing in renewable energy?", "What initiatives exist to support entrepreneurs in Qatar?", "How does Qatar plan to diversify its economy beyond oil and gas?", "What is Qatar’s plan for workforce development?" ] } ] }, { "title": "Summits & Conferences | القمم والمؤتمرات", "topics": [ { "name": "Qatar Economic Forum | منتدى قطر الاقتصادي", "date": "Annual | سنوي", "focus": "Discussing global economic trends and investment opportunities." }, { "name": "Qatar Climate Change Summit | قمة قطر للتغير المناخي", "date": "Annual | سنوي", "focus": "Addressing environmental sustainability and climate policies." } ] }, { "title": "Council of Ministers | مجلس الوزراء", "topics": [ { "name": "Sheikh Mohammed bin Abdulrahman Al Thani | الشيخ محمد بن عبدالرحمن آل ثاني", "position": "Prime Minister and Minister of Foreign Affairs | رئيس الوزراء ووزير الخارجية", "appointed": "March 2023 | مارس 2023" } ] }, { "title": "Recent News & Announcements | آخر الأخبار والإعلانات", "topics": [ { "title": "Cabinet approves draft law on artists and artistic professions", "date": "01/01/2025", "url": "https://www.gco.gov.qa/en/state-of-qatar/state-institutions/council-of-ministers/press-releases/" } ] } ]; // 🔍 **First, check if an answer exists in `scopeData` before using OpenAI** function findAnswerInScope(question) { const lowerCaseQuestion = question.toLowerCase(); for (const topic of scopeData) { if (topic.topics) { for (const subtopic of topic.topics) { for (const detail of subtopic.details || []) { if (typeof detail === "string" && lowerCaseQuestion.includes(detail.toLowerCase())) { console.log("✅ Found answer in scopeData:", detail); return detail; } } } } } return null; } // Process AI Chat Query // exports.processChatQuery = async (req, res) => { // try { // const { message, user_id, language } = req.body; // console.log("🔹 User Query:", message); // // Check if the question is within the allowed scope // if (!isQuestionInScope(message)) { // return res.json({ // response: "⚠️ Sorry, I can only answer questions within the allowed scope. Can you rephrase your question?" // }); // } // let botReply = ""; // let botReplyAr = ""; // let knowledgeId = null; // // First, try to find an answer in the knowledge base using FULLTEXT search // let [result] = await db.execute( // `SELECT id, answer, answer_ar FROM ai_knowledge_base WHERE MATCH(question) AGAINST(? IN NATURAL LANGUAGE MODE) LIMIT 1`, // [message] // ); // // If no FULLTEXT results, fallback to a LIKE search // if (result.length === 0) { // console.log("No FULLTEXT results. Trying LIKE search..."); // [result] = await db.execute( // "SELECT id, answer, answer_ar FROM ai_knowledge_base WHERE question LIKE ? LIMIT 1", // [`%${message}%`] // ); // } // if (result.length > 0) { // console.log("✅ Found in Knowledge Base:", result[0].answer); // botReply = result[0].answer; // botReplyAr = language === "ar" ? result[0].answer_ar : await translateText(botReply, "ar"); // knowledgeId = result[0].id; // } else { // console.log("❌ Not Found in Knowledge Base. Asking OpenAI..."); // botReply = await askOpenAI(message) || "⚠️ AI could not generate a response."; // botReplyAr = await translateText(botReply, "ar"); // // Save the new knowledge into the AI Knowledge Base // const [insertResult] = await db.execute( // "INSERT INTO ai_knowledge_base (question, question_ar, answer, answer_ar) VALUES (?, ?, ?, ?)", // [message, await translateText(message, "ar"), botReply, botReplyAr] // ); // knowledgeId = insertResult.insertId; // } // // Save chat history with the linked knowledge base entry // await saveChatHistory(user_id, message, botReply, botReplyAr, language, knowledgeId); // res.json({ response: botReply }); // } catch (error) { // console.error("❌ AI Chatbot Error:", error); // res.status(500).json({ error: "Chatbot failed to respond" }); // } // }; // above is old function // Helper Functions // 🔍 **Ensure OpenAI only answers about the summit** async function askOpenAI(question) { try { const systemMessage = `You are an AI assistant for the National Development Summit 2025, held at QNCC, Doha, on March 8, 2025. Answer only questions related to: - The summit (venue, date, speakers, registration, agenda). - Qatar’s official policies (Vision 2030, economic plans, government). - Travel, accommodation, and logistics. - Other official Qatari summits (Economic, Climate, Energy). 📌 **Response Guidelines:** - Keep answers **under 150 words**. - Focus **only on the exact question** asked. - Use bullet points for clarity. - Refer to Qatar’s official sources if unsure. ❌ **Avoid:** - Off-topic questions. - Unverified or speculative answers. - Unnecessary long responses. ✅ **Be clear, concise, and factual.**`; const response = await axios.post( "https://api.openai.com/v1/chat/completions", { model: "gpt-4", messages: [ { role: "system", content: systemMessage }, { role: "user", content: question } ], max_tokens: 300 // 150-200 words max }, { headers: { Authorization: `Bearer ${OPENAI_API_KEY}` } } ); return response?.data?.choices?.[0]?.message?.content?.trim(); } catch (err) { console.error("❌ OpenAI API Error:", err); return "⚠️ AI could not generate a response."; } } async function translateText(text, targetLang) { try { const response = await axios.post( "https://api.openai.com/v1/chat/completions", { model: "gpt-4", messages: [{ role: "user", content: `Translate to ${targetLang}: ${text}` }], }, { headers: { Authorization: `Bearer ${OPENAI_API_KEY}` } } ); return response?.data?.choices?.[0]?.message?.content?.trim(); } catch (err) { console.error("❌ Translation Error:", err); return "⚠️ Translation not available."; } } async function saveChatHistory(user_id, message, botReply, botReplyAr, language, knowledgeId) { try { console.log("📝 Saving Chat History..."); await db.execute( "INSERT INTO chat_history (user_id, user_question, user_question_ar, bot_response, bot_response_ar, knowledge_base_id) VALUES (?, ?, ?, ?, ?, ?)", [ user_id, language === "ar" ? null : message, language === "ar" ? message : await translateText(message, "ar"), language === "ar" ? null : botReply, botReplyAr, knowledgeId ] ); } catch (err) { console.error("❌ Error saving chat history:", err); } } function isQuestionInScope(question) { const lowerCaseQuestion = question.toLowerCase(); console.log("🔍 Checking question:", lowerCaseQuestion); // Keywords that should match event-related queries const eventKeywords = ["summit", "conference", "event", "national development", "qncc", "where is the summit", "tell me about the event"]; // Check if the question contains any event-related keyword if (eventKeywords.some(keyword => lowerCaseQuestion.includes(keyword))) { console.log("✅ Matched using event keywords!"); return true; } for (const topic of scopeData) { console.log("🛠️ Checking topic:", topic.title); // Direct match with the topic title if (topic.title && topic.title.toLowerCase().includes(lowerCaseQuestion)) { console.log("✅ Matched in title:", topic.title); return true; } // Match inside the overview descriptions if (topic.overview) { if ( (topic.overview.description_en && topic.overview.description_en.toLowerCase().includes(lowerCaseQuestion)) || (topic.overview.description_ar && topic.overview.description_ar.toLowerCase().includes(lowerCaseQuestion)) ) { console.log("✅ Matched in overview:", topic.title); return true; } } // Check inside topics array if (topic.topics) { for (const subtopic of topic.topics) { console.log("🔍 Checking subtopic:", subtopic.name); if ( (subtopic.name && subtopic.name.toLowerCase().includes(lowerCaseQuestion)) || (subtopic.details && subtopic.details.some(detail => detail.toLowerCase().includes(lowerCaseQuestion))) ) { console.log("✅ Matched in subtopic:", subtopic.name); return true; } } } // Check inside pillars if present if (topic.pillars) { for (const pillar of topic.pillars) { console.log("🔍 Checking pillar:", pillar.name); if (pillar.name && pillar.name.toLowerCase().includes(lowerCaseQuestion)) { console.log("✅ Matched in pillar:", pillar.name); return true; } if (pillar.description && pillar.description.toLowerCase().includes(lowerCaseQuestion)) { console.log("✅ Matched in pillar description:", pillar.description); return true; } if (pillar.focus_areas) { for (const area of pillar.focus_areas) { console.log("🔍 Checking focus area:", area.title); if (area.title && area.title.toLowerCase().includes(lowerCaseQuestion)) { console.log("✅ Matched in focus area:", area.title); return true; } if (area.details) { for (const detail of area.details) { if (typeof detail === "string" && detail.toLowerCase().includes(lowerCaseQuestion)) { console.log("✅ Matched in focus area detail:", detail); return true; } } } } } } } } console.log("❌ No match found. Out of scope."); return false; } function extractRelevantAnswer(fullAnswer, question) { const lowerCaseAnswer = fullAnswer.toLowerCase(); const lowerCaseQuestion = question.toLowerCase(); // 🔍 **If the question contains a specific keyword, extract only that section** if (lowerCaseAnswer.includes("qatar independence") && lowerCaseQuestion.includes("independence")) { return fullAnswer.match(/Qatar gained independence[^.]+\./i)?.[0] || fullAnswer; } if (lowerCaseAnswer.includes("venue") && lowerCaseQuestion.includes("venue")) { return fullAnswer.match(/Venue: [^.]+\./i)?.[0] || fullAnswer; } if (lowerCaseAnswer.includes("registration") && lowerCaseQuestion.includes("register")) { return fullAnswer.match(/Registration: [^.]+\./i)?.[0] || fullAnswer; } if (lowerCaseAnswer.includes("date") && lowerCaseQuestion.includes("date")) { return fullAnswer.match(/Date: [^.]+\./i)?.[0] || fullAnswer; } // 🔹 **If no match, return the shortest sentence** const sentences = fullAnswer.split(". "); return sentences.length > 1 ? sentences[0] + "." : fullAnswer; } exports.processChatQuery = async (req, res) => { try { const { message, user_id, language } = req.body; console.log("🔹 User Query:", message); let botReply = ""; let botReplyAr = ""; let knowledgeId = null; // 🔍 **Check if an answer exists in `ai_knowledge_base`** let [result] = await db.execute( "SELECT id, answer, answer_ar FROM ai_knowledge_base WHERE question LIKE ? LIMIT 1", [`%${message}%`] ); if (result.length > 0) { console.log("✅ Found in Knowledge Base:", result[0].answer); botReply = extractRelevantAnswer(result[0].answer, message); botReplyAr = language === "ar" ? result[0].answer_ar : await translateText(botReply, "ar"); knowledgeId = result[0].id; } else { // 🔹 **Check `scopeData` for direct answers** botReply = findAnswerInScope(message); if (!botReply) { console.log("❌ Not found in scopeData. Asking OpenAI..."); botReply = await askOpenAI(message) || "⚠️ AI could not generate a response."; } botReplyAr = await translateText(botReply, "ar"); // 🔹 **Save the new knowledge into `ai_knowledge_base`** const [insertResult] = await db.execute( "INSERT INTO ai_knowledge_base (question, question_ar, answer, answer_ar) VALUES (?, ?, ?, ?)", [message, await translateText(message, "ar"), botReply, botReplyAr] ); knowledgeId = insertResult.insertId; } // 🔹 **Save chat history** await saveChatHistory(user_id, message, botReply, botReplyAr, language, knowledgeId); res.json({ response: botReply }); } catch (error) { console.error("❌ AI Chatbot Error:", error); res.status(500).json({ error: "Chatbot failed to respond" }); } }; // Fetch All Knowledge Base Entries exports.getKnowledgeBase = async (req, res) => { try { const [results] = await db.execute("SELECT * FROM ai_knowledge_base"); res.json(results); } catch (err) { console.error("❌ Error fetching knowledge base:", err); res.status(500).json({ error: "Database error" }); } }; exports.getKnowledgeBasebyID = async (req, res) => { try { const { id } = req.params; const [results] = await db.execute("SELECT * FROM ai_knowledge_base WHERE id = ?", [id]); if (results.length === 0) { return res.status(404).json({ error: "Entry not found" }); } res.json(results[0]); } catch (err) { console.error("❌ Error fetching entry:", err); res.status(500).json({ error: "Database error" }); } }; // Add a New Entry to Knowledge Base exports.addKnowledgeBaseEntry = async (req, res) => { try { const { question, question_ar, answer, answer_ar } = req.body; await db.execute( "INSERT INTO ai_knowledge_base (question, question_ar, answer, answer_ar) VALUES (?, ?, ?, ?)", [question, question_ar, answer, answer_ar] ); res.json({ message: "✅ Training data added successfully!" }); } catch (err) { console.error("❌ Error adding knowledge base entry:", err); res.status(500).json({ error: "Database error" }); } }; // Update Knowledge Base Entry exports.updateKnowledgeBaseEntry = async (req, res) => { try { const { id } = req.params; const { question, question_ar, answer, answer_ar } = req.body; await db.execute( "UPDATE ai_knowledge_base SET question = ?, question_ar = ?, answer = ?, answer_ar = ? WHERE id = ?", [question, question_ar, answer, answer_ar, id] ); res.json({ message: "✅ Training data updated successfully!" }); } catch (err) { console.error("❌ Error updating knowledge base entry:", err); res.status(500).json({ error: "Database error" }); } }; // Delete Knowledge Base Entry exports.deleteKnowledgeBaseEntry = async (req, res) => { try { const { id } = req.params; await db.execute("DELETE FROM ai_knowledge_base WHERE id = ?", [id]); res.json({ message: "✅ Training data deleted successfully!" }); } catch (err) { console.error("❌ Error deleting knowledge base entry:", err); res.status(500).json({ error: "Database error" }); } };