Last active
April 19, 2025 22:35
-
-
Save weskerty/6aa65e1bd6f88fd77c2adce5aa081c7c to your computer and use it in GitHub Desktop.
Get Media Public Server
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
const fs = require('fs').promises; | |
const path = require('path'); | |
const { bot } = require('../lib'); | |
const config = { | |
media: { | |
directory: process.env.AMULEDOWNLOADS || path.join(process.cwd(), 'tmp'), | |
maxFilesToSend: 20, | |
maxFileSize: Number(process.env.MAX_UPLOAD || 1500) * 1048576 // 1.5 GB | |
}, | |
timeout: { | |
selection: 60000, // 1 minuto | |
}, | |
messages: { | |
fileTooLarge: "El archivo es demasiado grande para enviar. Max ({maxSize} MB).", | |
usageGuide: "Uso del comando:\n> gm <nombre> para buscar un archivo.\n> gm dd l1,l2,l3 para descargar archivos específicos de búsqueda local.", | |
noResultsFound: "No se encontraron resultados para: *{query}*.", | |
sendingFiles: "Enviando: \n{files}", | |
oversizedFiles: "Los siguientes archivos exceden el límite de tamaño ({size}) y no se enviaron:\n- {files}", | |
queueMessage: "⏳ Estás en cola. Posición: #{position}", | |
waitingForSelection: "ℹ️ Usa `gm dd` L1,l#,etc... para seleccionar. Tienes {timeout} segundos.", | |
searchQueued: "🔍 Tu búsqueda ha sido encolada. Posición: #{position}" | |
} | |
}; | |
// Tipos de archivo permitidos | |
const FILE_TYPES = { | |
video: { extensions: ['mp4', 'mkv', 'avi'], mimetype: 'video/mp4' }, | |
image: { extensions: ['jpg', 'jpeg', 'png', 'gif'], mimetype: 'image/jpeg' }, | |
document: { | |
extensions: ['pdf', 'epub', 'docx', 'txt', 'apk', 'apks'], | |
mimetypes: { | |
'pdf': 'application/pdf', | |
'epub': 'application/epub+zip', | |
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', | |
'txt': 'text/plain', | |
'apk': 'application/vnd.android.package-archive', | |
'default': 'application/octet-stream' | |
} | |
}, | |
audio: { extensions: ['mp3', 'wav', 'ogg', 'flac'], mimetype: 'audio/mpeg' } | |
}; | |
const globalQueue = { | |
activeSearches: 0, | |
searchQueue: [], | |
searchResults: new Map(), | |
searchTimeout: null, | |
addSearch(searchTask, message) { | |
if (this.activeSearches === 0) { | |
this.activeSearches = 1; | |
this.processSearch(searchTask, message); | |
return 0; | |
} else { | |
this.searchQueue.push({ searchTask, message }); | |
return this.searchQueue.length; | |
} | |
}, | |
async processSearch(searchTask, message) { | |
try { | |
await searchTask(); | |
} catch (error) { | |
console.error("Error procesando búsqueda:", error); | |
try { | |
await message.send(`⚠️ Error en la búsqueda: ${error.message}`, { quoted: message.data }); | |
} catch (replyError) { | |
console.error("Error al enviar mensaje de error:", replyError); | |
} | |
} | |
this.searchTimeout = setTimeout(() => { | |
this.completeSearch(); | |
}, config.timeout.selection); | |
}, | |
completeSearch() { | |
this.activeSearches = 0; | |
this.searchResults.clear(); | |
if (this.searchTimeout) { | |
clearTimeout(this.searchTimeout); | |
this.searchTimeout = null; | |
} | |
if (this.searchQueue.length > 0) { | |
const { searchTask, message } = this.searchQueue.shift(); | |
this.activeSearches = 1; | |
this.processSearch(searchTask, message); | |
} | |
}, | |
setResults(results) { | |
this.searchResults.clear(); | |
results.forEach(file => { | |
this.searchResults.set(file.index, file); | |
}); | |
}, | |
getSelectedFiles(indices) { | |
return indices | |
.filter(item => item.type === 'l') | |
.map(item => this.searchResults.get(item.index)) | |
.filter(file => file !== undefined); | |
}, | |
hasActiveSearch() { | |
return this.activeSearches > 0; | |
}, | |
clearResults() { | |
this.searchResults.clear(); | |
} | |
}; | |
const utils = { | |
formatMessage(message, replacements = {}) { | |
return Object.entries(replacements).reduce((formattedMsg, [placeholder, value]) => | |
formattedMsg.replace(new RegExp(`{${placeholder}}`, 'g'), value), message); | |
}, | |
async delay(ms) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
}, | |
parseIndices(input) { | |
if (!input) return []; | |
return input | |
.toLowerCase() | |
.split(/[,\s]+/) | |
.map(item => { | |
const match = item.match(/^([l])(\d+)$/i); | |
if (!match) return null; | |
return { | |
type: match[1].toLowerCase(), | |
index: parseInt(match[2]) | |
}; | |
}) | |
.filter(item => item !== null); | |
}, | |
formatTimeInSeconds(milliseconds) { | |
const seconds = Math.floor(milliseconds / 1000); | |
return seconds; | |
} | |
}; | |
class FileUtils { | |
static async getStats(filePath) { | |
try { | |
return await fs.stat(filePath); | |
} catch (error) { | |
console.error(`Error al verificar estadísticas del archivo ${filePath}:`, error); | |
return null; | |
} | |
} | |
static async getSize(filePath) { | |
const stats = await this.getStats(filePath); | |
return stats ? stats.size : null; | |
} | |
static async isSizeValid(filePath, maxSize) { | |
const size = await this.getSize(filePath); | |
return size !== null && size <= maxSize; | |
} | |
static formatSize(sizeInBytes) { | |
if (!sizeInBytes || isNaN(sizeInBytes)) return "Desconocido"; | |
const kb = sizeInBytes / 1024; | |
if (kb < 1024) return `${kb.toFixed(2)} KB`; | |
const mb = kb / 1024; | |
if (mb < 1024) return `${mb.toFixed(2)} MB`; | |
return `${(mb / 1024).toFixed(2)} GB`; | |
} | |
static getDetails(filePath) { | |
const ext = path.extname(filePath).slice(1).toLowerCase(); | |
for (const [category, typeInfo] of Object.entries(FILE_TYPES)) { | |
if (typeInfo.extensions.includes(ext)) { | |
return { | |
category, | |
mimetype: category === 'document' | |
? (typeInfo.mimetypes[ext] || typeInfo.mimetypes.default) | |
: typeInfo.mimetype | |
}; | |
} | |
} | |
return { | |
category: 'document', | |
mimetype: FILE_TYPES.document.mimetypes.default | |
}; | |
} | |
static async search(directory, keyword, results = []) { | |
try { | |
const entries = await fs.readdir(directory, { withFileTypes: true }); | |
await Promise.all(entries.map(async entry => { | |
const fullPath = path.join(directory, entry.name); | |
if (entry.isDirectory()) { | |
await this.search(fullPath, keyword, results); | |
} else if (entry.name.toLowerCase().includes(keyword.toLowerCase())) { | |
results.push(fullPath); | |
} | |
})); | |
} catch (error) { | |
console.error(`Error: Buscando en ${directory}: ${error.message}`); | |
} | |
return results; | |
} | |
} | |
bot( | |
{ | |
pattern: 'gm ?(.*)', | |
fromMe: true, | |
desc: 'Buscar y enviar archivos en el directorio de medios', | |
type: 'download', | |
}, | |
async (message, match) => { | |
const query = (match || '').trim(); | |
const reply = async (text, isError = false) => { | |
try { | |
await message.send(`${isError ? '⚠️' : '✅'} ${text}`, { quoted: message.data }); | |
} catch (replyError) { | |
console.error('Error: Error en Respuesta', replyError); | |
} | |
}; | |
try { | |
if (!query) { | |
return await reply(config.messages.usageGuide, true); | |
} | |
if (query.startsWith('dd')) { | |
if (!globalQueue.hasActiveSearch()) { | |
return await reply("No hay una búsqueda activa para seleccionar archivos.", true); | |
} | |
return await handleSelection(query.slice(2).trim(), message, reply); | |
} | |
const queuePosition = globalQueue.addSearch( | |
async () => { | |
await handleSearch(query, message, reply); | |
}, | |
message | |
); | |
if (queuePosition > 0) { | |
await reply(utils.formatMessage(config.messages.searchQueued, { position: queuePosition })); | |
} | |
} catch (error) { | |
console.error('Error GM Plugin:', error); | |
await message.send(`⚠️ ${error.message}`, { quoted: message.data }); | |
} | |
} | |
); | |
async function handleSearch(query, message, reply) { | |
try { | |
const foundFiles = await FileUtils.search(config.media.directory, query); | |
if (foundFiles.length === 0) { | |
globalQueue.completeSearch(); | |
return await reply(utils.formatMessage(config.messages.noResultsFound, { query }), true); | |
} | |
const fileResults = await Promise.all(foundFiles.map(async (filePath, index) => { | |
const size = await FileUtils.getSize(filePath); | |
const formattedSize = size ? FileUtils.formatSize(size) : "Desconocido"; | |
return { | |
index: index + 1, | |
path: filePath, | |
name: path.basename(filePath), | |
size, | |
formattedSize | |
}; | |
})); | |
globalQueue.setResults(fileResults); | |
const timeoutSeconds = utils.formatTimeInSeconds(config.timeout.selection); | |
const selectionMessage = utils.formatMessage(config.messages.waitingForSelection, { | |
timeout: timeoutSeconds | |
}); | |
let resultMessage = `${selectionMessage}\n✅ Resultados Local (${fileResults.length}):\n`; | |
fileResults.forEach(file => { | |
resultMessage += `\`L${file.index}.\` ${file.name} \n> Tamaño ${file.formattedSize}\n`; | |
}); | |
await reply(resultMessage); | |
} catch (error) { | |
console.error('Error en búsqueda:', error); | |
await reply(`Error en búsqueda: ${error.message}`, true); | |
globalQueue.completeSearch(); | |
} | |
} | |
async function handleSelection(selectionStr, message, reply) { | |
try { | |
const selectedIndices = utils.parseIndices(selectionStr); | |
if (selectedIndices.length === 0) { | |
return await reply("Formato de selección incorrecto. Ejemplo: l1,l2,l3", true); | |
} | |
const selectedFiles = globalQueue.getSelectedFiles(selectedIndices); | |
if (selectedFiles.length === 0) { | |
return await reply("No se encontraron archivos válidos en la selección.", true); | |
} | |
await reply(utils.formatMessage(config.messages.sendingFiles, { | |
files: selectedFiles.map(file => `${file.name} (${file.formattedSize})`).join('\n') | |
})); | |
const oversizedFiles = []; | |
for (const file of selectedFiles) { | |
try { | |
const isValid = await FileUtils.isSizeValid(file.path, config.media.maxFileSize); | |
if (!isValid) { | |
oversizedFiles.push(file.name); | |
continue; | |
} | |
const fileBuffer = await fs.readFile(file.path); | |
const { mimetype, category } = FileUtils.getDetails(file.path); | |
const fileStats = await FileUtils.getStats(file.path); | |
const creationDate = fileStats ? new Date(fileStats.birthtime).toLocaleString() : "Fecha desconocida"; | |
await message.send( | |
fileBuffer, | |
{ | |
fileName: file.name, | |
mimetype, | |
caption: `Fecha de creación: ${creationDate}`, | |
quoted: message.data | |
}, | |
category | |
); | |
} catch (fileError) { | |
console.error(`Error: Enviando Archivo ${file.path}:`, fileError); | |
await reply(`Error al enviar archivo ${file.name}: ${fileError.message}`, true); | |
} | |
} | |
if (oversizedFiles.length > 0) { | |
await reply(utils.formatMessage(config.messages.oversizedFiles, { | |
size: FileUtils.formatSize(config.media.maxFileSize), | |
files: oversizedFiles.join('\n- ') | |
}), true); | |
} | |
globalQueue.completeSearch(); | |
} catch (error) { | |
console.error("Error procesando selección:", error); | |
await reply(`Error en selección: ${error.message}`, true); | |
globalQueue.completeSearch(); | |
} | |
} | |
module.exports = {}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment