Last active
October 19, 2025 13:30
-
-
Save weskerty/1ca1027f34e28cb90f7a76841ae971dd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // dla.js | |
| // Copyright (C) 2025 Weskerty | |
| // | |
| // Este programa se distribuye bajo los términos de la Licencia Pública General Affero de GNU (AGPLv3). | |
| // Usted puede usarlo, modificarlo y redistribuirlo bajo esa licencia. | |
| // Este software se proporciona SIN GARANTÍA alguna. | |
| // Licencia completa: https://www.gnu.org/licenses/agpl-3.0.html | |
| // CAMBIO DE GR A GQ por conflicto con palabras en Español | |
| const FormData = require('form-data'); | |
| const { bot, logger } = require('../lib'); | |
| class RequestError extends Error { | |
| constructor(message, type = 'GenericError') { | |
| super(message); | |
| this.name = type; | |
| } | |
| } | |
| const utils = { | |
| cleanCommand: (text) => text.replace(/^gr\s*/i, '').trim(), | |
| sanitizeInput: (input) => input.substring(0, 2000), | |
| combineMessages: (commandText, quotedText) => { | |
| if (!commandText && !quotedText) return ''; | |
| if (!commandText) return quotedText; | |
| if (!quotedText) return commandText; | |
| return `${commandText}\n\nTexto citado: ${quotedText}`; | |
| } | |
| }; | |
| async function apiRequest(endpoint, method, headers, body, config, retries = 3) { | |
| const fetch = (await import('node-fetch')).default; | |
| const completeHeaders = { | |
| ...headers, | |
| 'Authorization': `Bearer ${config.GROQ_API_KEY}`, | |
| }; | |
| for (let attempt = 1; attempt <= retries; attempt++) { | |
| try { | |
| const response = await fetch(endpoint, { | |
| method, | |
| headers: completeHeaders, | |
| body, | |
| }); | |
| if (!response.ok) { | |
| const errorText = await response.text(); | |
| throw new RequestError(`API Error: ${response.status} - ${errorText}`, 'APIError'); | |
| } | |
| return await response.json(); | |
| } catch (error) { | |
| logger.error(`Request attempt ${attempt} failed:`, error); | |
| if (attempt === retries) throw error; | |
| await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt))); | |
| } | |
| } | |
| } | |
| const mediaHandler = { | |
| async transcribeAudio(mediaBuffer, config) { | |
| const formData = new FormData(); | |
| formData.append('model', config.GROQ_AUDIO_MODEL); | |
| formData.append('file', mediaBuffer, { | |
| filename: 'audio.ogg', | |
| contentType: 'audio/ogg', | |
| }); | |
| try { | |
| const result = await apiRequest( | |
| 'https://api.groq.com/openai/v1/audio/transcriptions', | |
| 'POST', | |
| formData.getHeaders(), | |
| formData, | |
| config | |
| ); | |
| return result.text?.trim() || 'No transcription available'; | |
| } catch (error) { | |
| logger.error('Audio transcription failed', error); | |
| throw error; | |
| } | |
| }, | |
| async analyzeImage(mediaBuffer, userMessage, config) { | |
| const imageDataUrl = `data:image/jpeg;base64,${mediaBuffer.toString('base64')}`; | |
| const apiBody = { | |
| messages: [ | |
| { | |
| role: 'user', | |
| content: [ | |
| { type: 'text', text: userMessage || 'explain this' }, | |
| { type: 'image_url', image_url: { url: imageDataUrl } }, | |
| ], | |
| }, | |
| ], | |
| model: config.GROQ_IMAGE_MODEL, | |
| temperature: 1, | |
| max_tokens: 1024, | |
| top_p: 1, | |
| stream: false, | |
| stop: null, | |
| }; | |
| try { | |
| const result = await apiRequest( | |
| 'https://api.groq.com/openai/v1/chat/completions', | |
| 'POST', | |
| { 'Content-Type': 'application/json' }, | |
| JSON.stringify(apiBody), | |
| config | |
| ); | |
| return result.choices[0].message.content.trim(); | |
| } catch (error) { | |
| logger.error('Image analysis failed', error); | |
| throw error; | |
| } | |
| }, | |
| }; | |
| async function processRequest(message, userMessage, ctx) { | |
| try { | |
| const config = { | |
| GROQ_API_KEY: ctx.GROQ_API_KEY, | |
| GROQ_MODEL: ctx.GROQ_MODEL || 'llama-3.3-70b-versatile', | |
| GROQ_SYSTEM_MSG: ctx.GROQ_SYSTEM_MSG || 'Eres LEvanterBot.', | |
| GROQ_IMAGE_MODEL: ctx.GROQ_IMAGE_MODEL || 'meta-llama/llama-4-maverick-17b-128e-instruct', | |
| GROQ_AUDIO_MODEL: ctx.GROQ_AUDIO_MODEL || 'whisper-large-v3', | |
| }; | |
| if (!config.GROQ_API_KEY) { | |
| throw new RequestError('> ❌ You can set `setvar GROQ_API_KEY = APIKEY` \n Key in: https://console.groq.com/keys', 'AuthenticationError'); | |
| } | |
| let quotedText = ''; | |
| if (message.reply_message && message.reply_message.text && | |
| !message.reply_message.mimetype?.startsWith('audio') && | |
| !message.reply_message.mimetype?.startsWith('image')) { | |
| quotedText = message.reply_message.text; | |
| } | |
| if (message.reply_message?.mimetype?.startsWith('audio')) { | |
| const mediaBuffer = await message.reply_message.downloadMediaMessage(); | |
| const transcription = await mediaHandler.transcribeAudio(mediaBuffer, config); | |
| if (userMessage) { | |
| const combinedMessage = utils.combineMessages(userMessage, transcription); | |
| return handleTextRequest( | |
| message, | |
| combinedMessage, | |
| { ...config, GROQ_SYSTEM_MSG: config.GROQ_SYSTEM_MSG } | |
| ); | |
| } else { | |
| return message.send(transcription, { quoted: message.quoted }); | |
| } | |
| } | |
| if (message.reply_message?.mimetype?.startsWith('image')) { | |
| const mediaBuffer = await message.reply_message.downloadMediaMessage(); | |
| const analysisResult = await mediaHandler.analyzeImage( | |
| mediaBuffer, | |
| userMessage, | |
| config | |
| ); | |
| return message.send(analysisResult, { quoted: message.quoted }); | |
| } | |
| if (!userMessage && !quotedText) { | |
| return message.send( | |
| '>*Example :\n- gr What is the capital of France?\n- gr Whats in this image?(reply to a image)\n- gr Analyze this text (reply to a text message)', | |
| { quoted: message.quoted } | |
| ); | |
| } | |
| if (userMessage && quotedText) { | |
| return handleTextRequest( | |
| message, | |
| quotedText, | |
| { ...config, GROQ_SYSTEM_MSG: userMessage } | |
| ); | |
| } | |
| const finalMessage = userMessage || quotedText; | |
| return handleTextRequest(message, finalMessage, config); | |
| } catch (error) { | |
| logger.error('Request processing error', error); | |
| message.send(`❌ ${error.message || 'Unexpected error occurred'}`, { quoted: message.quoted }); | |
| } | |
| } | |
| async function handleTextRequest(message, userMessage, config) { | |
| const apiBody = { | |
| messages: [ | |
| { role: 'system', content: config.GROQ_SYSTEM_MSG }, | |
| { role: 'user', content: utils.sanitizeInput(userMessage) }, | |
| ], | |
| model: config.GROQ_MODEL, | |
| temperature: 1, | |
| max_tokens: 2024, | |
| top_p: 1, | |
| stream: false, | |
| stop: null, | |
| }; | |
| const result = await apiRequest( | |
| 'https://api.groq.com/openai/v1/chat/completions', | |
| 'POST', | |
| { 'Content-Type': 'application/json' }, | |
| JSON.stringify(apiBody), | |
| config | |
| ); | |
| return message.send(result.choices[0].message.content.trim(), { quoted: message.quoted }); | |
| } | |
| bot( | |
| { | |
| pattern: 'gq ?(.*)', | |
| desc: 'Transcribe audio, Images, TXT.', | |
| type: 'AI', | |
| }, | |
| async (message, match, ctx) => { | |
| const cleanedMatch = utils.cleanCommand(match); | |
| return processRequest(message, cleanedMatch, ctx); | |
| } | |
| ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment