Jarvis-md, chatbot by blix-xer A developer operating under the handle blix-xer published Jarvis-md, a WhatsApp chatbot built on the Baileys library that routes text and image queries to Groq-hosted models, using openai/gpt-oss-20b for text and qwen/qwen3.8-27b for vision. The bot keeps a rolling 10-message conversation history per chat, caps responses at 700 tokens, and includes a keyword-triggered motivational speech feature that returns one of 15 hardcoded two-line messages. | | 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." | | | ; | | | } | | | } ; |