# Jarvis-md, chatbot by blix-xer

> Source: <https://gist.github.com/blix-xer/231f66478b3c695ea5050271851fd4bb>
> Published: 2026-11-06 20:59:25+00:00

|  | const { | 
|  | System, | 
|  | getData, | 
|  | setData, | 
|  | makeUrl | 
|  | } = require("../lib/"); | 
|  | // ============================================= | 
|  | // GROQ CONFIGURATION | 
|  | // ============================================= | 
|  | const GROQ_API = | 
|  | "https://api.groq.com/openai/v1/chat/completions"; | 
|  | const GROQ_API_KEY = process.env.GROQ_API_KEY \|\| ""; | 
|  | const TEXT_MODEL = "openai/gpt-oss-20b"; | 
|  | const VISION_MODEL = "qwen/qwen3.8-27b"; | 
|  | const MAX_HISTORY = 10; | 
|  | const MAX_TOKENS = 700; | 
|  | const TIMEZONE = "Asia/Kolkata"; | 
|  | const conversations = new Map(); | 
|  | // ============================================= | 
|  | // OWNER SETTINGS | 
|  | // ============================================= | 
|  | const OWNER_NAME = "BLIX-XER"; | 
|  | const OWNER_NUMBER = "918075298454"; | 
|  | // ============================================= | 
|  | // MOTIVATIONAL SPEECH COLLECTION | 
|  | // ============================================= | 
|  | const MOTIVATION_SPEECHES = [ | 
|  | [ | 
|  | "Your current situation is not your final destination.", | 
|  | "Keep moving forward; your future is still yours to create." | 
|  | ], | 
|  | [ | 
|  | "Greatness begins when you stop waiting for the perfect moment.", | 
|  | "Take the first step today, and let your actions build tomorrow." | 
|  | ], | 
|  | [ | 
|  | "Your dreams deserve more than just your imagination.", | 
|  | "Give them your effort, your patience, and your determination." | 
|  | ], | 
|  | [ | 
|  | "Every challenge is an opportunity to discover your strength.", | 
|  | "Rise above the difficulty and prove what you are capable of." | 
|  | ], | 
|  | [ | 
|  | "Do not let one difficult chapter define your entire story.", | 
|  | "You still have countless pages left to write." | 
|  | ], | 
|  | [ | 
|  | "Success is built through small efforts repeated consistently.", | 
|  | "Keep showing up, even when nobody notices your progress." | 
|  | ], | 
|  | [ | 
|  | "The road to greatness is not always easy.", | 
|  | "But every step you take makes you stronger than yesterday." | 
|  | ], | 
|  | [ | 
|  | "Stop measuring your journey against someone else's timeline.", | 
|  | "Build your own path and move at your own pace." | 
|  | ], | 
|  | [ | 
|  | "A powerful future begins with a decision made today.", | 
|  | "Choose courage over excuses and progress over perfection." | 
|  | ], | 
|  | [ | 
|  | "Your mistakes are lessons, not permanent labels.", | 
|  | "Learn from them, stand again, and keep building your future." | 
|  | ], | 
|  | [ | 
|  | "You do not need to have everything figured out today.", | 
|  | "You only need the courage to take your next step." | 
|  | ], | 
|  | [ | 
|  | "Let your determination speak louder than your doubts.", | 
|  | "One day, your hard work will tell a story worth remembering." | 
|  | ], | 
|  | [ | 
|  | "Never underestimate what consistent effort can accomplish.", | 
|  | "Small steps taken every day can lead to extraordinary places." | 
|  | ], | 
|  | [ | 
|  | "The strongest progress often happens when nobody is watching.", | 
|  | "Keep improving yourself, even when the applause is absent." | 
|  | ], | 
|  | [ | 
|  | "Do not surrender your dreams because the journey feels difficult.", | 
|  | "Some of the most meaningful achievements take time." | 
|  | ] | 
|  | ]; | 
|  | // ============================================= | 
|  | // MOTIVATION REQUEST DETECTION | 
|  | // ============================================= | 
|  | function isMotivationRequest(text) { | 
|  | const input = String(text \|\| "") | 
|  | .toLowerCase() | 
|  | .trim(); | 
|  | const patterns = [ | 
|  | /\bgive me (some )?motivation\b/, | 
|  | /\bmotivate me\b/, | 
|  | /\bi need motivation\b/, | 
|  | /\bneed some motivation\b/, | 
|  | /\bmotivation speech\b/, | 
|  | /\bsay something motivational\b/, | 
|  | /\binspire me\b/, | 
|  | /\bgive me an? motivational\b/, | 
|  | /\bmotivation please\b/, | 
|  | /\bmotivation venam\b/, | 
|  | /\bmotivation tharamo\b/, | 
|  | /\boru motivation\b/, | 
|  | /\benik motivation\b/, | 
|  | /\benikku motivation\b/, | 
|  | /\bkurach motivation\b/, | 
|  | /\bmotivate cheyy\b/, | 
|  | /പ്രചോദനം/, | 
|  | /മോട്ടിവേഷൻ/, | 
|  | /എനിക്ക് മോട്ടിവേഷൻ/ | 
|  | ]; | 
|  | return patterns.some(pattern => pattern.test(input)); | 
|  | } | 
|  | // ============================================= | 
|  | // GET A DIFFERENT MOTIVATIONAL SPEECH | 
|  | // ============================================= | 
|  | function getMotivationSpeech(history) { | 
|  | const recentAnswers = (history \|\| []) | 
|  | .filter(item => item.role === "assistant") | 
|  | .map(item => item.content); | 
|  | const unusedSpeeches = MOTIVATION_SPEECHES.filter( | 
|  | speech => { | 
|  | const formatted = | 
|  | speech.join("\n") + | 
|  | "\n\n*— JARVIS (Original)*"; | 
|  | return !recentAnswers.includes(formatted); | 
|  | } | 
|  | ); | 
|  | const available = | 
|  | unusedSpeeches.length > 0 | 
|  | ? unusedSpeeches | 
|  | : MOTIVATION_SPEECHES; | 
|  | const selected = | 
|  | available[ | 
|  | Math.floor(Math.random() * available.length) | 
|  | ]; | 
|  | return ( | 
|  | selected[0] + | 
|  | "\n" + | 
|  | selected[1] + | 
|  | "\n\n" + | 
|  | "*— JARVIS (Original)*" | 
|  | ); | 
|  | } | 
|  | // ============================================= | 
|  | // CURRENT DATE AND TIME | 
|  | // ============================================= | 
|  | function getCurrentDateTime() { | 
|  | const now = new Date(); | 
|  | const date = new Intl.DateTimeFormat("en-IN", { | 
|  | timeZone: TIMEZONE, | 
|  | weekday: "long", | 
|  | day: "2-digit", | 
|  | month: "long", | 
|  | year: "numeric" | 
|  | }).format(now); | 
|  | const time = new Intl.DateTimeFormat("en-IN", { | 
|  | timeZone: TIMEZONE, | 
|  | hour: "2-digit", | 
|  | minute: "2-digit", | 
|  | second: "2-digit", | 
|  | hour12: true | 
|  | }).format(now); | 
|  | return { | 
|  | date, | 
|  | time, | 
|  | timezone: "Indian Standard Time (IST)" | 
|  | }; | 
|  | } | 
|  | // ============================================= | 
|  | // HELPER FUNCTIONS | 
|  | // ============================================= | 
|  | function getNumber(jid) { | 
|  | if (!jid) return ""; | 
|  | return String(jid) | 
|  | .split("@")[0] | 
|  | .split(":")[0] | 
|  | .replace(/\D/g, ""); | 
|  | } | 
|  | function isOwner(jid) { | 
|  | return getNumber(jid) === getNumber(OWNER_NUMBER); | 
|  | } | 
|  | function getQuotedName(quoted) { | 
|  | if (!quoted) return null; | 
|  | return ( | 
|  | quoted.pushName \|\| | 
|  | quoted.notify \|\| | 
|  | quoted.name \|\| | 
|  | quoted.senderName \|\| | 
|  | quoted.contact?.name \|\| | 
|  | null | 
|  | ); | 
|  | } | 
|  | function getQuotedSender(quoted) { | 
|  | if (!quoted) return ""; | 
|  | return ( | 
|  | quoted.sender \|\| | 
|  | quoted.participant \|\| | 
|  | quoted.key?.participant \|\| | 
|  | quoted.from \|\| | 
|  | "" | 
|  | ); | 
|  | } | 
|  | // ============================================= | 
|  | // GROQ API FUNCTION | 
|  | // ============================================= | 
|  | async function askGroq(model, messages) { | 
|  | if (!GROQ_API_KEY) { | 
|  | throw new Error("GROQ_API_KEY is missing."); | 
|  | } | 
|  | const response = await fetch(GROQ_API, { | 
|  | method: "POST", | 
|  | headers: { | 
|  | Authorization: `Bearer ${GROQ_API_KEY}`, | 
|  | "Content-Type": "application/json" | 
|  | }, | 
|  | body: JSON.stringify({ | 
|  | model, | 
|  | messages, | 
|  | max_completion_tokens: MAX_TOKENS, | 
|  | temperature: 0.7, | 
|  | stream: false | 
|  | }), | 
|  | signal: AbortSignal.timeout(60000) | 
|  | }); | 
|  | if (!response.ok) { | 
|  | const errorText = await response.text(); | 
|  | console.error( | 
|  | "JARVIS Groq Error:", | 
|  | response.status, | 
|  | errorText.slice(0, 1500) | 
|  | ); | 
|  | throw new Error( | 
|  | `Groq API returned ${response.status}` | 
|  | ); | 
|  | } | 
|  | const data = await response.json(); | 
|  | const answer = | 
|  | data.choices?.[0]?.message?.content?.trim(); | 
|  | if (!answer) { | 
|  | throw new Error("Groq returned an empty response."); | 
|  | } | 
|  | return answer; | 
|  | } | 
|  | // ============================================= | 
|  | // JARVIS PERSONALITY | 
|  | // ============================================= | 
|  | function createSystemPrompt({ | 
|  | userName, | 
|  | owner, | 
|  | quoted, | 
|  | quotedName, | 
|  | quotedOwner | 
|  | }) { | 
|  | const current = getCurrentDateTime(); | 
|  | return ` | 
|  | You are JARVIS, an intelligent, friendly, | 
|  | natural and highly capable WhatsApp AI assistant. | 
|  | ======================== | 
|  | OWNER INFORMATION | 
|  | ======================== | 
|  | Owner name: ${OWNER_NAME} | 
|  | Current user: ${userName} | 
|  | Is current user the owner: ${owner ? "YES" : "NO"} | 
|  | IDENTITY RULES: | 
|  | 1. The owner's name is always ${OWNER_NAME}. | 
|  | 2. If the owner asks "What is my name?", | 
|  | answer: "Your name is ${OWNER_NAME}." | 
|  | 3. If someone asks who owns you, | 
|  | answer: "My owner is ${OWNER_NAME}." | 
|  | 4. Never call another user your owner. | 
|  | 5. Never invent a person's name. | 
|  | 6. Use the available WhatsApp display name naturally. | 
|  | ======================== | 
|  | QUOTED MESSAGE IDENTITY | 
|  | ======================== | 
|  | Quoted message exists: ${quoted ? "YES" : "NO"} | 
|  | Quoted sender name: ${quotedName \|\| "Unavailable"} | 
|  | Is quoted sender the owner: ${quotedOwner ? "YES" : "NO"} | 
|  | If the user asks who sent the quoted message, | 
|  | use the quoted sender information. | 
|  | Never guess an unavailable identity. | 
|  | ======================== | 
|  | LIVE DATE AND TIME | 
|  | ======================== | 
|  | Current date: ${current.date} | 
|  | Current time: ${current.time} | 
|  | Timezone: ${current.timezone} | 
|  | RULES: | 
|  | 1. Use this supplied date and time as the current reference. | 
|  | 2. Always use Asia/Kolkata timezone. | 
|  | 3. Understand questions about today's date. | 
|  | 4. Understand questions about the current time. | 
|  | 5. Understand questions about the current weekday. | 
|  | 6. Calculate tomorrow and yesterday correctly. | 
|  | 7. Understand questions about other dates, months and years. | 
|  | 8. Never invent the current date or time. | 
|  | 9. This information is refreshed for every incoming message. | 
|  | ======================== | 
|  | ADVANCED KERALA LANGUAGE INTELLIGENCE | 
|  | ======================== | 
|  | Understand natural Kerala Malayalam, Manglish and English. | 
|  | SUPPORTED INPUT: | 
|  | 1. Malayalam script. | 
|  | 2. Manglish using English letters. | 
|  | 3. English. | 
|  | 4. Malayalam-English mixed sentences. | 
|  | 5. Casual Kerala slang and colloquial expressions. | 
|  | MANGLISH UNDERSTANDING: | 
|  | Manglish has no single fixed spelling. | 
|  | Examples: | 
|  | - "sugham ano?" | 
|  | - "sukham aano?" | 
|  | - "sugham aano?" | 
|  | - "sukhamano?" | 
|  | These can mean "സുഖമാണോ?" | 
|  | More examples: | 
|  | - "nee evideya?" = "നീ എവിടെയാ?" | 
|  | - "entha parupadi?" = "എന്താ പരിപാടി?" | 
|  | - "enik ariyilla" = "എനിക്ക് അറിയില്ല" | 
|  | - "avan entha paranje?" = "അവൻ എന്താ പറഞ്ഞത്?" | 
|  | - "njan varam" = "ഞാൻ വരാം" | 
|  | - "onnu help cheyyamo?" = "ഒന്ന് ഹെൽപ്പ് ചെയ്യാമോ?" | 
|  | IMPORTANT RULES: | 
|  | 1. Never require perfect Manglish spelling. | 
|  | 2. Understand words based on the full sentence. | 
|  | 3. Recognize different spellings of the same word. | 
|  | 4. Understand casual Kerala conversation. | 
|  | 5. Understand common abbreviations and informal typing. | 
|  | 6. Do not interpret every Manglish word literally. | 
|  | 7. Use conversation context to understand unclear words. | 
|  | 8. Never invent a meaning if genuinely unclear. | 
|  | 9. Ask a short clarification question when necessary. | 
|  | RESPONSE LANGUAGE RULES: | 
|  | 1. Malayalam input → Reply in natural Malayalam. | 
|  | 2. Manglish input → Reply in natural Manglish using English letters. | 
|  | 3. English input → Reply in natural English. | 
|  | 4. Mixed input → Reply in the same natural mixed style. | 
|  | 5. Never convert Manglish into Malayalam script unless requested. | 
|  | 6. Never convert Malayalam into Manglish unless requested. | 
|  | 7. Do not sound like a translation engine. | 
|  | 8. Use natural Kerala conversational expressions. | 
|  | 9. Avoid unnecessarily formal Malayalam. | 
|  | 10. Preserve the user's original meaning and tone. | 
|  | EXAMPLES: | 
|  | User: "eda nee evideya?" | 
|  | Good response: "Njan ivide thanne undu. Enthada vendathu?" | 
|  | User: "enik oru karyam parayan und" | 
|  | Good response: "Parayoo, entha karyam?" | 
|  | Your goal is to understand how real Kerala users | 
|  | communicate, not just textbook Malayalam. | 
|  | ======================== | 
|  | MOTIVATIONAL INTELLIGENCE | 
|  | ======================== | 
|  | Understand requests for motivation in English, | 
|  | Malayalam and Manglish. | 
|  | Examples: | 
|  | - Give me motivation. | 
|  | - Motivate me. | 
|  | - I need motivation. | 
|  | - Give me a motivational speech. | 
|  | - Motivation tharamo? | 
|  | - Enik oru motivation venam. | 
|  | - Kurach motivation parayamo? | 
|  | - എനിക്ക് ഒരു മോട്ടിവേഷൻ തരാമോ? | 
|  | When a user requests motivation: | 
|  | 1. Give a powerful and meaningful motivational speech. | 
|  | 2. The speech must contain exactly two motivational lines. | 
|  | 3. Include an author attribution separately. | 
|  | 4. Use the attribution "JARVIS (Original)" for original speeches. | 
|  | 5. Never falsely attribute original writing to famous people. | 
|  | 6. Keep the message inspiring, positive and memorable. | 
|  | 7. Avoid repeating recent motivational speeches. | 
|  | 8. Match the user's language when generating motivation. | 
|  | 9. Do not add unnecessary introductions or explanations. | 
|  | ======================== | 
|  | IMAGE UNDERSTANDING | 
|  | ======================== | 
|  | When an image is provided, analyze it carefully. | 
|  | Capabilities: | 
|  | - Object recognition | 
|  | - Animals and vehicles | 
|  | - Products and devices | 
|  | - Places and scenery | 
|  | - Colors and visual details | 
|  | - Image descriptions | 
|  | - OCR and text extraction | 
|  | - Screenshots and documents | 
|  | - Visual question answering | 
|  | - Visible logos and symbols | 
|  | IMAGE RULES: | 
|  | 1. Answer using visible evidence. | 
|  | 2. Read visible text accurately. | 
|  | 3. If text is unclear, say so. | 
|  | 4. Never invent image details. | 
|  | 5. Distinguish certainty from guesses. | 
|  | 6. Never identify a real person in an image. | 
|  | 7. Do not infer sensitive personal characteristics. | 
|  | 8. Reply in the user's language. | 
|  | ======================== | 
|  | PERSONALITY | 
|  | ======================== | 
|  | - Speak naturally and conversationally. | 
|  | - Be intelligent, friendly and helpful. | 
|  | - Understand follow-up questions. | 
|  | - Use previous conversation context. | 
|  | - Understand jokes and casual language. | 
|  | - Give concise answers unless detail is needed. | 
|  | - Avoid unnecessary repetition. | 
|  | - Never reveal API keys or private configuration. | 
|  | - Never invent facts. | 
|  | - Admit when you do not know something. | 
|  | - Avoid unnecessary emojis. | 
|  | You are JARVIS. Maintain this personality. | 
|  | `; | 
|  | } | 
|  | // ============================================= | 
|  | // CHATBOT COMMAND MENU | 
|  | // ============================================= | 
|  | function chatbotMenu() { | 
|  | return ( | 
|  | "*J A R V I S A I*\n" + | 
|  | "━━━━━━━━━━━━━━━━━━\n\n" + | 
|  | "*AVAILABLE COMMANDS*\n\n" + | 
|  | "1. *.chatbot on*\n" + | 
|  | " Activate JARVIS AI\n\n" + | 
|  | "2. *.chatbot off*\n" + | 
|  | " Deactivate JARVIS AI\n\n" + | 
|  | "━━━━━━━━━━━━━━━━━━\n" + | 
|  | "*ACCESS: OWNER ONLY*\n" + | 
|  | "*SYSTEM: JARVIS CHATBOT*" | 
|  | ); | 
|  | } | 
|  | // ============================================= | 
|  | // CHATBOT ON / OFF COMMAND | 
|  | // ============================================= | 
|  | System({ | 
|  | pattern: "chatbot", | 
|  | fromMe: true, | 
|  | desc: "Turn JARVIS AI on or off", | 
|  | type: "misc" | 
|  | }, async (message, match) => { | 
|  | const command = String(match \|\| "") | 
|  | .trim() | 
|  | .toLowerCase(); | 
|  | if (command !== "on" && command !== "off") { | 
|  | return message.send(chatbotMenu()); | 
|  | } | 
|  | const enabled = | 
|  | command === "on" ? "true" : "false"; | 
|  | await setData( | 
|  | message.user.id, | 
|  | "status", | 
|  | enabled, | 
|  | "chatBot" | 
|  | ); | 
|  | return message.send( | 
|  | command === "on" | 
|  | ? ( | 
|  | "*J A R V I S A I*\n" + | 
|  | "━━━━━━━━━━━━━━━━━━\n\n" + | 
|  | "*STATUS: ACTIVATED*\n\n" + | 
|  | "JARVIS AI is now online and ready.\n" + | 
|  | "━━━━━━━━━━━━━━━━━━" | 
|  | ) | 
|  | : ( | 
|  | "*J A R V I S A I*\n" + | 
|  | "━━━━━━━━━━━━━━━━━━\n\n" + | 
|  | "*STATUS: DEACTIVATED*\n\n" + | 
|  | "JARVIS AI has been switched off.\n" + | 
|  | "━━━━━━━━━━━━━━━━━━" | 
|  | ) | 
|  | ); | 
|  | }); | 
|  | // ============================================= | 
|  | // AUTOMATIC AI CHATBOT | 
|  | // ============================================= | 
|  | System({ | 
|  | on: "text", | 
|  | fromMe: false, | 
|  | dontAddCommandList: true | 
|  | }, async (m) => { | 
|  | try { | 
|  | // ===================================== | 
|  | // MESSAGE CHECKS | 
|  | // ===================================== | 
|  | if (m.isBot \|\| !m.body) return; | 
|  | // In groups, only respond to replies. | 
|  | if (m.isGroup && !m.reply_message) return; | 
|  | const { chatBot } = | 
|  | await getData(m.user.id); | 
|  | if (!chatBot \|\| chatBot.status !== "true") { | 
|  | return; | 
|  | } | 
|  | const mode = chatBot.message \|\| "chat"; | 
|  | if (mode === "pm" && m.isGroup) return; | 
|  | if (mode === "group" && !m.isGroup) return; | 
|  | // ===================================== | 
|  | // GROQ API KEY CHECK | 
|  | // ===================================== | 
|  | if (!GROQ_API_KEY) { | 
|  | console.error( | 
|  | "JARVIS: Missing GROQ_API_KEY." | 
|  | ); | 
|  | return m.reply( | 
|  | "*JARVIS AI CONFIGURATION ERROR*\n" + | 
|  | "━━━━━━━━━━━━━━━━━━\n\n" + | 
|  | "*GROQ API KEY IS MISSING!*\n\n" + | 
|  | "Please configure your Groq API key in Render Environment Variables.\n\n" + | 
|  | "*GET YOUR GROQ API KEY:*\n" + | 
|  | "https://console.groq.com/keys\n\n" + | 
|  | "*ENVIRONMENT VARIABLE:*\n" + | 
|  | "GROQ_API_KEY\n\n" + | 
|  | "━━━━━━━━━━━━━━━━━━\n" + | 
|  | "*JARVIS SYSTEM*" | 
|  | ); | 
|  | } | 
|  | // ===================================== | 
|  | // CURRENT USER IDENTITY | 
|  | // ===================================== | 
|  | const sender = m.sender; | 
|  | const owner = isOwner(sender); | 
|  | const userName = owner | 
|  | ? OWNER_NAME | 
|  | : (m.pushName \|\| "Unknown"); | 
|  | // ===================================== | 
|  | // QUOTED MESSAGE IDENTITY | 
|  | // ===================================== | 
|  | const quoted = m.reply_message; | 
|  | const quotedSender = getQuotedSender(quoted); | 
|  | const quotedOwner = quotedSender | 
|  | ? isOwner(quotedSender) | 
|  | : false; | 
|  | const quotedName = quoted | 
|  | ? ( | 
|  | quotedOwner | 
|  | ? OWNER_NAME | 
|  | : getQuotedName(quoted) | 
|  | ) | 
|  | : null; | 
|  | // ===================================== | 
|  | // CONVERSATION MEMORY | 
|  | // ===================================== | 
|  | const historyKey = | 
|  | `${m.user.id}:${sender}`; | 
|  | const history = | 
|  | conversations.get(historyKey) \|\| []; | 
|  | const userText = String(m.body).trim(); | 
|  | // ===================================== | 
|  | // MOTIVATIONAL SPEECH FEATURE | 
|  | // ===================================== | 
|  | if (isMotivationRequest(userText)) { | 
|  | const motivation = | 
|  | getMotivationSpeech(history); | 
|  | const updatedHistory = [ | 
|  | ...history, | 
|  | { | 
|  | role: "user", | 
|  | content: userText | 
|  | }, | 
|  | { | 
|  | role: "assistant", | 
|  | content: motivation | 
|  | } | 
|  | ]; | 
|  | conversations.set( | 
|  | historyKey, | 
|  | updatedHistory.slice(-MAX_HISTORY) | 
|  | ); | 
|  | return await m.reply(motivation); | 
|  | } | 
|  | // ===================================== | 
|  | // QUOTED IMAGE PROCESSING | 
|  | // ===================================== | 
|  | let imageUrl = null; | 
|  | if (quoted && quoted.image) { | 
|  | try { | 
|  | console.log( | 
|  | "JARVIS: Quoted image detected." | 
|  | ); | 
|  | const imagePath = | 
|  | await quoted.downloadAndSave(); | 
|  | if (!imagePath) { | 
|  | throw new Error( | 
|  | "Image download returned no file." | 
|  | ); | 
|  | } | 
|  | imageUrl = await makeUrl(imagePath); | 
|  | if ( | 
|  | !imageUrl \|\| | 
|  | !/^https?:\/\//i.test(imageUrl) | 
|  | ) { | 
|  | throw new Error( | 
|  | "Invalid uploaded image URL." | 
|  | ); | 
|  | } | 
|  | console.log( | 
|  | "JARVIS: Image uploaded successfully." | 
|  | ); | 
|  | } catch (imageError) { | 
|  | console.error( | 
|  | "JARVIS Image Error:", | 
|  | imageError | 
|  | ); | 
|  | return m.reply( | 
|  | "I couldn't process that image. Please try again." | 
|  | ); | 
|  | } | 
|  | } | 
|  | // ===================================== | 
|  | // SYSTEM PROMPT | 
|  | // ===================================== | 
|  | const systemPrompt = createSystemPrompt({ | 
|  | userName, | 
|  | owner, | 
|  | quoted, | 
|  | quotedName, | 
|  | quotedOwner | 
|  | }); | 
|  | // ===================================== | 
|  | // PREPARE MESSAGES | 
|  | // ===================================== | 
|  | const messages = [ | 
|  | { | 
|  | role: "system", | 
|  | content: systemPrompt | 
|  | }, | 
|  | ...history.slice(-MAX_HISTORY) | 
|  | ]; | 
|  | // ===================================== | 
|  | // VISION REQUEST | 
|  | // ===================================== | 
|  | if (imageUrl) { | 
|  | messages.push({ | 
|  | role: "user", | 
|  | content: [ | 
|  | { | 
|  | type: "text", | 
|  | text: userText \|\| | 
|  | "Describe this image and explain what is visible." | 
|  | }, | 
|  | { | 
|  | type: "image_url", | 
|  | image_url: { | 
|  | url: imageUrl | 
|  | } | 
|  | } | 
|  | ] | 
|  | }); | 
|  | } else { | 
|  | // ================================= | 
|  | // NORMAL TEXT REQUEST | 
|  | // ================================= | 
|  | messages.push({ | 
|  | role: "user", | 
|  | content: userText | 
|  | }); | 
|  | } | 
|  | // ===================================== | 
|  | // SELECT MODEL | 
|  | // ===================================== | 
|  | const selectedModel = imageUrl | 
|  | ? VISION_MODEL | 
|  | : TEXT_MODEL; | 
|  | console.log( | 
|  | `JARVIS: Using model ${selectedModel}` | 
|  | ); | 
|  | // ===================================== | 
|  | // GROQ REQUEST | 
|  | // ===================================== | 
|  | const answer = await askGroq( | 
|  | selectedModel, | 
|  | messages | 
|  | ); | 
|  | // ===================================== | 
|  | // SAVE HISTORY | 
|  | // ===================================== | 
|  | const updatedHistory = [ | 
|  | ...history, | 
|  | { | 
|  | role: "user", | 
|  | content: imageUrl | 
|  | ? `${userText}\n[Image analyzed]` | 
|  | : userText | 
|  | }, | 
|  | { | 
|  | role: "assistant", | 
|  | content: answer | 
|  | } | 
|  | ]; | 
|  | conversations.set( | 
|  | historyKey, | 
|  | updatedHistory.slice(-MAX_HISTORY) | 
|  | ); | 
|  | // ===================================== | 
|  | // SEND RESPONSE | 
|  | // ===================================== | 
|  | return await m.reply(answer); | 
|  | } catch (error) { | 
|  | console.error( | 
|  | "JARVIS Chatbot Error:", | 
|  | error | 
|  | ); | 
|  | return m.reply( | 
|  | "JARVIS encountered an unexpected error. Please try again." | 
|  | ); | 
|  | } | 
|  | }); |
