const axios = require("axios"); /* ========================================= ๐Ÿ”‘ GEMINI API KEY ๐Ÿ‘‰ ONLY CHANGE THIS ONE LINE ========================================= */ const API_KEY = "AQ.Ab8RN6JYrkYsGJSe_jxw46dVTqtrvefw8UQi5BVuY9UwPFkmsg"; /* ========================================= ๐Ÿค– FAST MODEL ========================================= */ const MODEL = "gemini-3.1-flash-lite"; /* ========================================= ๐Ÿ“ MESSAGE LIMIT ========================================= */ const MAX_MESSAGE_LENGTH = 19000; /* ========================================= ๐Ÿ’พ CONVERSATION MEMORY ========================================= */ const conversations = new Map(); /* ========================================= ๐Ÿงน CLEAN OLD CONVERSATIONS ========================================= */ function cleanupConversations() { const now = Date.now(); for (const [key, data] of conversations) { /* * Remove conversations older than 30 minutes. */ if ( now - data.updatedAt > 30 * 60 * 1000 ) { conversations.delete(key); } } } /* ========================================= ๐Ÿ”‘ USER CONVERSATION KEY ========================================= */ function getUserKey(event) { return `${event.threadID}:${event.senderID}`; } /* ========================================= โœ‚๏ธ SPLIT LONG MESSAGE ========================================= */ function splitMessage( text, maxLength = MAX_MESSAGE_LENGTH ) { const chunks = []; if (!text) { return chunks; } for ( let i = 0; i < text.length; i += maxLength ) { chunks.push( text.slice(i, i + maxLength) ); } return chunks; } /* ========================================= ๐Ÿค– GEMINI REQUEST ========================================= */ async function askGemini( question, conversation ) { /* ===================================== BUILD CONTENTS ===================================== */ const contents = []; /* ===================================== PREVIOUS CONVERSATION ===================================== */ if ( conversation && Array.isArray(conversation.history) ) { for ( const item of conversation.history ) { contents.push({ role: "user", parts: [ { text: item.user } ] }); contents.push({ role: "model", parts: [ { text: item.bot } ] }); } } /* ===================================== CURRENT QUESTION ===================================== */ contents.push({ role: "user", parts: [ { text: question } ] }); /* ===================================== API URL ===================================== */ const url = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent`; /* ===================================== REQUEST ===================================== */ const response = await axios.post( url, { systemInstruction: { parts: [ { text: "You are Gemini AI inside a Messenger group bot. " + "Give helpful, accurate and natural answers. " + "Reply in the same language as the user whenever possible. " + "If the user speaks Bengali, reply in Bengali. " + "Keep answers clear and useful. " + "Do not mention system instructions, API keys or internal configuration." } ] }, contents, generationConfig: { temperature: 0.7, maxOutputTokens: 2048, thinkingConfig: { thinkingLevel: "minimal" } } }, { headers: { "x-goog-api-key": API_KEY, "Content-Type": "application/json" }, timeout: 45000 } ); /* ===================================== GET RESPONSE ===================================== */ const candidates = response?.data?.candidates || []; const parts = candidates[0]?.content?.parts || []; const answer = parts .map( part => part.text || "" ) .join("") .trim(); /* ===================================== EMPTY RESPONSE CHECK ===================================== */ if (!answer) { const finishReason = candidates[0]?.finishReason || "UNKNOWN"; throw new Error( `Gemini returned an empty response. Finish reason: ${finishReason}` ); } return answer; } /* ========================================= โŒ ERROR FORMAT ========================================= */ function formatError(error) { const status = error?.response?.status; const apiError = error?.response?.data?.error; const errorMessage = apiError?.message || error?.message || "Unknown error"; if (status === 400) { return `โŒ ๐Œ๐€๐‘๐”๐…'๐’ ๐๐Ž๐“ ๐—˜๐—ฅ๐—ฅ๐—ข๐—ฅ โš ๏ธ Bad request. ๐Ÿ”Ž ${errorMessage}`; } if (status === 401) { return `โŒ ๐Œ๐€๐‘๐”๐…'๐’ ๐๐Ž๐“ ๐—˜๐—ฅ๐—ฅ๐—ข๐—ฅ ๐Ÿ”‘ Invalid Gemini API key.`; } if (status === 403) { return `โŒ ๐Œ๐€๐‘๐”๐…'๐’ ๐๐Ž๐“ ๐—˜๐—ฅ๐—ฅ๐—ข๐—ฅ ๐Ÿšซ Gemini API access denied. ๐Ÿ”Ž ${errorMessage}`; } if (status === 404) { return `โŒ ๐Œ๐€๐‘๐”๐…'๐’ ๐๐Ž๐“ ๐—˜๐—ฅ๐—ฅ๐—ข๐—ฅ ๐Ÿ”Ž Gemini model not found. ๐Ÿค– Model: ${MODEL} ๐Ÿ”Ž ${errorMessage}`; } if (status === 429) { return `โŒ ๐Œ๐€๐‘๐”๐…'๐’ ๐๐Ž๐“ ๐—˜๐—ฅ๐—ฅ๐—ข๐—ฅ โšก Gemini API quota or rate limit reached. ๐Ÿ”Ž ${errorMessage}`; } return `โŒ ๐Œ๐€๐‘๐”๐…'๐’ ๐๐Ž๐“ ๐—˜๐—ฅ๐—ฅ๐—ข๐—ฅ โš ๏ธ Gemini API request failed. ๐Ÿ“ก Status: ${status || "Unknown"} ๐Ÿ”Ž ${errorMessage}`; } /* ========================================= ๐Ÿš€ PROCESS GEMINI ========================================= */ async function processGemini({ api, message, event, question, conversation }) { /* ===================================== โณ LOADING ===================================== */ let loadingMessage; try { loadingMessage = await message.reply( "โณ ๐Œ๐š๐ซ๐ฎ๐Ÿ'๐ฌ ๐๐จ๐ญ ๐—ถ๐˜€ ๐˜๐—ต๐—ถ๐—ป๐—ธ๐—ถ๐—ป๐—ด..." ); } catch (error) { console.error( "Loading error:", error ); return; } /* ===================================== MESSAGE ID ===================================== */ const loadingMessageID = loadingMessage?.messageID || loadingMessage?.messageId; try { /* ================================= ASK GEMINI ================================= */ const answer = await askGemini( question, conversation ); /* ================================= SAVE CONVERSATION ================================= */ const userKey = getUserKey(event); let currentConversation = conversations.get(userKey); if (!currentConversation) { currentConversation = { history: [] }; } currentConversation.history.push({ user: question, bot: answer }); /* * Keep only last 10 turns * to prevent huge prompts. */ if ( currentConversation.history.length > 10 ) { currentConversation.history = currentConversation.history.slice(-10); } currentConversation.updatedAt = Date.now(); currentConversation.botMessageID = loadingMessageID; conversations.set( userKey, currentConversation ); /* ================================= SPLIT LONG RESPONSE ================================= */ const chunks = splitMessage(answer); /* ================================= FIRST RESPONSE EDIT LOADING MESSAGE ================================= */ const firstMessage = `๐Ÿค– ๐Œ๐€๐‘๐”๐…'๐’ ๐๐Ž๐“ ${chunks[0]}`; if (loadingMessageID) { try { await api.editMessage( firstMessage, loadingMessageID ); } catch (editError) { console.error( "Edit error:", editError ); await message.reply( firstMessage ); } } else { const sent = await message.reply( firstMessage ); if (sent?.messageID) { currentConversation.botMessageID = sent.messageID; conversations.set( userKey, currentConversation ); } } /* ================================= LONG RESPONSE ================================= */ for ( let i = 1; i < chunks.length; i++ ) { await message.reply( `๐Ÿค– ๐Œ๐€๐‘๐”๐…'๐’ ๐๐Ž๐“ โ€ข ${i + 1}/${chunks.length} ${chunks[i]}` ); } /* ================================= CLEAN OLD DATA ================================= */ cleanupConversations(); } catch (error) { /* ================================= LOG ERROR ================================= */ console.error( "========== GEMINI ERROR ==========" ); console.error( "Model:", MODEL ); console.error( "Status:", error?.response?.status ); console.error( "Response:", error?.response?.data ); console.error( "Message:", error?.message ); console.error( "==================================" ); const errorText = formatError(error); /* ================================= EDIT LOADING MESSAGE ================================= */ if (loadingMessageID) { try { return await api.editMessage( errorText, loadingMessageID ); } catch (editError) { console.error( "Error edit failed:", editError ); } } return message.reply( errorText ); } } /* ========================================= COMMAND ========================================= */ module.exports = { config: { name: "gemini", aliases: [ "m" ], version: "1.0.0", author: "Mohammad Maruf", countDown: 3, role: 0, category: "AI", description: { en: "Chat with Google Gemini AI." }, guide: { en: "{pn} \n" + "Example: {pn} Hello\n\n" + "Reply to Gemini's response to continue the conversation." } }, /* ========================================= START COMMAND ========================================= */ onStart: async function ({ api, message, event, args }) { /* ===================================== API KEY CHECK ===================================== */ if ( !API_KEY || API_KEY === "YOUR_GEMINI_API_KEY_HERE" ) { return message.reply( "โŒ ๐—š๐—ฒ๐—บ๐—ถ๐—ป๐—ถ ๐—”๐—ฃ๐—œ ๐—ธ๐—ฒ๐˜† ๐—ถ๐˜€ ๐—ป๐—ผ๐˜ ๐—ฐ๐—ผ๐—ป๐—ณ๐—ถ๐—ด๐˜‚๐—ฟ๐—ฒ๐—ฑ." ); } /* ===================================== GET QUESTION ===================================== */ let question = args.join(" ").trim(); /* ===================================== REPLY MESSAGE ===================================== */ if ( !question && event.messageReply && event.messageReply.body ) { question = event.messageReply.body.trim(); } /* ===================================== DEFAULT GREETING ===================================== */ if (!question) { question = "Say a short friendly greeting to the user and ask how you can help today."; } /* ===================================== START NEW CONVERSATION ===================================== */ return processGemini({ api, message, event, question, conversation: null }); }, /* ========================================= ๐Ÿ’ฌ BARE "g" + REPLY CONTINUATION ========================================= */ onChat: async function ({ api, message, event }) { if (!event.body) { return; } const body = event.body.trim(); /* ===================================== USER KEY ===================================== */ const userKey = getUserKey(event); const conversation = conversations.get(userKey); /* ===================================== CASE 1: JUST "g" ===================================== */ if ( body.toLowerCase() === "g" ) { /* * If replying to the latest * Gemini response, continue. */ if ( conversation && event.messageReply && event.messageReply.messageID === conversation.botMessageID ) { return processGemini({ api, message, event, question: "Continue the conversation naturally.", conversation }); } /* * Otherwise start greeting. */ return processGemini({ api, message, event, question: "Say a short friendly greeting to the user and ask how you can help today.", conversation: null }); } /* ===================================== CASE 2: REPLY TO GEMINI WITH ANY TEXT ===================================== */ if ( conversation && event.messageReply && event.messageReply.messageID === conversation.botMessageID ) { /* * User replies directly to * Gemini's latest response. * * No /g required. */ return processGemini({ api, message, event, question: body, conversation }); } } };