├── .vscode └── settings.json ├── Procfile ├── src ├── img │ ├── 1558388864509.jpg │ ├── avatares │ │ └── .gitkeep │ ├── temp.png │ └── bayke │ │ ├── dvd.gif │ │ ├── boing.gif │ │ ├── ditto.gif │ │ ├── spin.gif │ │ ├── thanos.gif │ │ └── laserman.gif ├── modules │ ├── messages │ │ ├── invite.js │ │ ├── help.js │ │ ├── search.js │ │ ├── pick.js │ │ ├── stock.js │ │ ├── 8b.js │ │ ├── mrc.js │ │ ├── embed.js │ │ └── despedir.js │ ├── cmd │ │ ├── ai.js │ │ ├── test.js │ │ ├── mrc.js │ │ ├── 8b.js │ │ ├── yt.js │ │ ├── dolar.js │ │ ├── bayke.js │ │ ├── pick.js │ │ ├── time.js │ │ ├── pepe.js │ │ ├── news.js │ │ ├── avatar.js │ │ ├── despedir.js │ │ ├── stock.js │ │ ├── so.js │ │ ├── search.js │ │ ├── cripto.js │ │ ├── urban.js │ │ ├── help.js │ │ ├── def.js │ │ ├── cn.js │ │ └── ripper.js │ └── index.js ├── utils │ ├── flow.js │ ├── numbers.js │ └── bot.js ├── services │ ├── chileanPesoService.js │ └── dolarService.js └── bot.js ├── .env.example ├── .prettierrc.json ├── bot.service ├── .github └── workflows │ └── DO-deployment.yaml ├── package.json ├── LICENSE.md ├── README.md └── .gitignore /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: npm start -------------------------------------------------------------------------------- /src/img/1558388864509.jpg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/avatares/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JGPenaB/Pasante-bot/HEAD/src/img/temp.png -------------------------------------------------------------------------------- /src/img/bayke/dvd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JGPenaB/Pasante-bot/HEAD/src/img/bayke/dvd.gif -------------------------------------------------------------------------------- /src/img/bayke/boing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JGPenaB/Pasante-bot/HEAD/src/img/bayke/boing.gif -------------------------------------------------------------------------------- /src/img/bayke/ditto.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JGPenaB/Pasante-bot/HEAD/src/img/bayke/ditto.gif -------------------------------------------------------------------------------- /src/img/bayke/spin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JGPenaB/Pasante-bot/HEAD/src/img/bayke/spin.gif -------------------------------------------------------------------------------- /src/img/bayke/thanos.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JGPenaB/Pasante-bot/HEAD/src/img/bayke/thanos.gif -------------------------------------------------------------------------------- /src/img/bayke/laserman.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JGPenaB/Pasante-bot/HEAD/src/img/bayke/laserman.gif -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | TOKEN=your_token 2 | SO_KEY=your_key 3 | NEWSKEY=your_key 4 | IEX_KEY=your_key 5 | CMC_KEY=your_key 6 | RAPIDAPI_KEY=your_key 7 | PREFIX=! 8 | HOST_ADDR=localhost 9 | HOST_PORT=8000 -------------------------------------------------------------------------------- /src/modules/messages/invite.js: -------------------------------------------------------------------------------- 1 | const messages = [ 2 | 'Aquí está mi currículum si quieres contratarme:', 3 | 'Aquí están mis datos:', 4 | 'Este es el link de invitación:' 5 | ]; 6 | 7 | module.exports = messages; 8 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "none", 4 | "arrowParens": "always", 5 | "endOfLine":"crlf", 6 | "printWidth":120, 7 | "tabWidth": 2, 8 | "semi": true 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/flow.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Detiene la ejecución por el tiempo dado 3 | * 4 | * @param { int } time tiempo en milisegundos 5 | * 6 | * @return { Promise } 7 | */ 8 | const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time)); 9 | 10 | module.exports = { sleep }; 11 | -------------------------------------------------------------------------------- /src/modules/messages/help.js: -------------------------------------------------------------------------------- 1 | const messages = [ 2 | 'Bueno manao, así es el beta.', 3 | 'Así es como se usa el comando que pediste.', 4 | 'El comando se usa de la siguiente manera.', 5 | 'El comando tiene estas instrucciones.', 6 | 'El betulio es así manaurem.', 7 | 'Así es como se hace la vaina, menor.' 8 | ]; 9 | 10 | module.exports = messages; 11 | -------------------------------------------------------------------------------- /src/modules/messages/search.js: -------------------------------------------------------------------------------- 1 | const messages = [ 2 | 'Esta es la imagen que encontré.', 3 | 'Esto fue lo primero que me salió en el gugul pirata.', 4 | 'Aquí tiene la imagen que pidió.', 5 | 'El primer resultado en Bing es este.', 6 | 'Laureano Marquez buscó e investigó, y llegó a la conclusión que esta es la mierda que buscas.' 7 | ]; 8 | 9 | module.exports = messages; 10 | -------------------------------------------------------------------------------- /bot.service: -------------------------------------------------------------------------------- 1 | t] 2 | Description=My app 3 | 4 | [Service] 5 | ExecStart=/root/Pasante-bot/src/bot.js 6 | Restart=always 7 | User=nobody 8 | # Note Debian/Ubuntu uses 'nogroup', RHEL/Fedora uses 'nobody' 9 | Group=nogroup 10 | Environment=PATH=/usr/bin/node 11 | Environment=NODE_ENV=production 12 | WorkingDirectory=/root/Pasante-bot/src 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /src/modules/messages/pick.js: -------------------------------------------------------------------------------- 1 | const messages = [ 2 | 'Marico **__USERNAME__** yo te recomiendo elegir: **__OPTION__**.', 3 | 'Mano **__USERNAME__** hoy jugue un animalito y la pegue, asi que te recomiendo: **__OPTION__**', 4 | '**__USERNAME__** segun los astros, lo mejor para ti es: **__OPTION__**', 5 | '**__USERNAME__** si tanta confianza me tienes entonces vete por: **__OPTION__**' 6 | ]; 7 | 8 | module.exports = messages; 9 | -------------------------------------------------------------------------------- /.github/workflows/DO-deployment.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy to DO 2 | on: 3 | push: 4 | branches: [master] 5 | 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Deploy pasante bot 11 | uses: appleboy/ssh-action@v1.0.3 12 | with: 13 | host: ${{ secrets.SSH_HOST }} 14 | key: ${{ secrets.SSH_KEY }} 15 | username: ${{ secrets.SSH_USERNAME }} 16 | script: | 17 | cd /Pasante-bot/ 18 | git pull origin master 19 | forever restart 0 -------------------------------------------------------------------------------- /src/modules/messages/stock.js: -------------------------------------------------------------------------------- 1 | const messages = [ 2 | 'Soy la nueva pasante, me llamo Coralba Tucson, te traje la info de', 3 | 'Hello, I am the new intern, my name is Lana Rhoades, here is the info of', 4 | 'Привет, я Bunny Colby, и я принес тебе информацию о', 5 | 'こんにちは、私は新しいインターンです. 私の名前は Marica Hase. の情報をお届けしました.', 6 | 'Je suis la nouvelle stagiaire, Anissa Kate, je vous ai apporté les info de', 7 | 'Sono la nuova stagista, Valentina Nappi, ti ho portato le info di', 8 | 'Siri, dame la información de', 9 | 'Te conseguí todo acerca de', 10 | 'Aqui tienes la info de' 11 | ]; 12 | 13 | module.exports = messages; 14 | -------------------------------------------------------------------------------- /src/modules/messages/8b.js: -------------------------------------------------------------------------------- 1 | const messages = [ 2 | 'En mi opinión, sí', 3 | 'Es cierto', 4 | 'Es decididamente así', 5 | 'Probablemente', 6 | 'Buen pronóstico', 7 | 'Todo apunta a que sí', 8 | 'Sin duda', 9 | 'Sí', 10 | 'Sí - definitivamente', 11 | 'Debes confiar en ello', 12 | 'Respuesta vaga, vuelve a intentarlo', 13 | 'Pregunta en otro momento', 14 | 'Será mejor que no te lo diga ahora', 15 | 'No puedo predecirlo ahora', 16 | 'Concéntrate y vuelve a preguntar', 17 | 'Puede ser', 18 | 'No cuentes con ello', 19 | 'Mi respuesta es no', 20 | 'Mis fuentes me dicen que no', 21 | 'Las perspectivas no son buenas', 22 | 'Muy dudoso' 23 | ]; 24 | 25 | module.exports = messages; 26 | -------------------------------------------------------------------------------- /src/modules/messages/mrc.js: -------------------------------------------------------------------------------- 1 | const messages = [ 2 | 'Bueno, me dijeron que **__USERNAME__** es sendo pato. No lo pongo en duda.', 3 | '¿Sabían que a **__USERNAME__** le gustan los hombres?', 4 | 'A **__USERNAME__** le gusta que le den por la puerta trasera.', 5 | 'A **__USERNAME__** le gusta que lo hagan cagar pa dentro.', 6 | 'Oí que **__USERNAME__** mea sentada.', 7 | 'A **__USERNAME__** se le moja la canoa.', 8 | '**__USERNAME__** es senda piaso e bicha.', 9 | '**__USERNAME__** piaso e puta.', 10 | '**__USERNAME__** muchacho marico.', 11 | 'A **__USERNAME__** le gustan los garrotes.', 12 | 'A **__USERNAME__** le limpian las cañerias a diario.' 13 | ]; 14 | 15 | module.exports = messages; 16 | -------------------------------------------------------------------------------- /src/services/chileanPesoService.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const { formatNumber } = require('../utils/numbers'); 3 | 4 | /** 5 | * Obtiene las tasas de los pesos chiles (CLP) 6 | * 7 | * @return { Array } 8 | */ 9 | const getExchangeRates = async () => { 10 | const list = []; 11 | 12 | await axios.get(`https://mindicador.cl/api`).then((response) => { 13 | const clpExchangeRate = response.data.dolar.valor ?? 0; 14 | const title = 'API chileca'; 15 | 16 | if (!isNaN(clpExchangeRate)) list.push({ title, rate: formatNumber(Number(clpExchangeRate)), value: clpExchangeRate, nationalCurrency: 'CLP' }); 17 | }); 18 | 19 | return list; 20 | }; 21 | 22 | module.exports = { getExchangeRates }; 23 | -------------------------------------------------------------------------------- /src/modules/cmd/ai.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | 3 | /** 4 | * Lista de alias válidos para el comando 5 | * 6 | * @return { Array } 7 | */ 8 | const aliases = () => ['ai']; 9 | 10 | /** 11 | * Información sobre el comando 12 | * 13 | * @return { Object } 14 | */ 15 | const help = () => ({ 16 | usage: '!ai {query}', 17 | desc: 'test', 18 | example: 'test' 19 | }); 20 | 21 | /** 22 | * Manejador del comando 23 | * 24 | * @param { Message } message Evento completo del mensaje 25 | * @param { string } userName Nombre del usuario que triggereó el evento 26 | */ 27 | const main = (message, userName) => { 28 | return message.channel.send(`test`); 29 | }; 30 | 31 | module.exports = { aliases, help, main }; 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pasante-bot", 3 | "version": "3.0.0", 4 | "description": "Bot de Discord para hacer estupideces", 5 | "main": "bot.js", 6 | "author": "Autz64, Darioxlz, Watermelonnable", 7 | "scripts": { 8 | "start": "node src/bot.js", 9 | "dev": "nodemon src/bot.js", 10 | "format": "prettier --write \"./**/*.js\"" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.21.0", 14 | "cheerio": "^1.0.0-rc.3", 15 | "discord.js": "^12.5.0", 16 | "dotenv": "^8.2.0", 17 | "puppeteer": "^21.1.1", 18 | "request": "^2.88.0", 19 | "rimraf": "^3.0.2", 20 | "winston": "^3.3.3", 21 | "zip-a-folder": "0.0.12" 22 | }, 23 | "devDependencies": { 24 | "nodemon": "^2.0.6", 25 | "prettier": "2.2.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/numbers.js: -------------------------------------------------------------------------------- 1 | // Todas las funciones relacionadas con numeros. 2 | 3 | /** 4 | * Genera un número aleatorio entre 0 y el límite 5 | * 6 | * @param { number } limit Número máximo de aleatoreidad 7 | * 8 | * @return { number } 9 | */ 10 | const randomWithLimit = (limit) => Math.floor(Math.random() * limit); 11 | 12 | /** 13 | * Restringe a 2 decimales y coloca un separador de miles 14 | * 15 | * @param { number } num 16 | * 17 | * @return { string } 18 | */ 19 | const decimalFix = (num) => (Math.round(num * 100) / 100).toLocaleString(); 20 | 21 | /** 22 | * Formatea un número y lo pone bonito 23 | * 24 | * @param { number } formattingNumber 25 | * 26 | * @return { number } 27 | */ 28 | const formatNumber = (formattingNumber) => new Intl.NumberFormat().format(formattingNumber); 29 | 30 | module.exports = { randomWithLimit, decimalFix, formatNumber }; 31 | -------------------------------------------------------------------------------- /src/modules/messages/embed.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Le indica al usuario cómo debe usar el comando 3 | * 4 | * @param { string } recommendation Qué debe hacer el usuario para usar bien el comando 5 | * @param { string } commandUsage Cómo debe usar correctamente el comando 6 | * 7 | * @return { Object } 8 | */ 9 | const generateEmbed = (recommendation, commandUsage) => ({ 10 | color: 5396735, 11 | fields: [ 12 | { 13 | name: 'Querido usuario', 14 | value: `Lamento informarle que aparte de que bayke tiene trabajo, usted debe ${recommendation}` 15 | }, 16 | { 17 | name: 'Uso', 18 | value: commandUsage 19 | } 20 | ], 21 | image: { 22 | url: 23 | 'https://www.bkconnection.com/system/refinery/blog/posts/thumbnails/000/003/323/post_detail/family-friendly-app-store.gif?1432824720' 24 | } 25 | }); 26 | 27 | module.exports = generateEmbed; 28 | -------------------------------------------------------------------------------- /src/utils/bot.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | 3 | const flow = require('./flow'); 4 | 5 | /** 6 | * Detiene la ejecución por el tiempo dado 7 | * 8 | * @param { Message } event El evento completo del mensaje 9 | * @param { string } message El mensaje a enviar 10 | * 11 | * @return { void } 12 | */ 13 | const simulateTyping = async (event, message) => { 14 | event.channel.startTyping(); 15 | 16 | await flow.sleep(message.length * 40); 17 | 18 | event.channel.stopTyping(); 19 | 20 | event.channel.send(message); 21 | }; 22 | 23 | /** 24 | * Obtiene los parámetros de un comando 25 | * 26 | * @param { String } message El mensaje del cual extraer los parámetros 27 | * 28 | * @return { String } 29 | */ 30 | const getParams = (message) => { 31 | const pos = message.indexOf(' '); 32 | let result; 33 | 34 | if (pos !== -1) { 35 | result = message.substring(pos + 1).trim(); 36 | } 37 | 38 | return result; 39 | }; 40 | 41 | module.exports = { simulateTyping, getParams }; 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jesús Gabriel Peña 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/modules/cmd/test.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | 3 | /** 4 | * Lista de alias válidos para el comando 5 | * 6 | * @return { Array } 7 | */ 8 | const aliases = () => ['test']; 9 | 10 | /** 11 | * Información sobre el comando 12 | * 13 | * @return { Object } 14 | */ 15 | const help = () => ({ 16 | usage: '!test', 17 | desc: 'Te dice cuántas fallas tiene tu mugroso código.', 18 | example: '!test' 19 | }); 20 | 21 | /** 22 | * Manejador del comando 23 | * 24 | * @param { Message } message Evento completo del mensaje 25 | * @param { string } userName Nombre del usuario que triggereó el evento 26 | */ 27 | const main = (message) => { 28 | const tests = Math.floor(Math.random() * 8) + 1; 29 | const assertions = Math.floor(Math.random() * 255) + 1; 30 | 31 | let emoji = message.guild.emojis.cache.find((emoji) => emoji.name === 'pepekek'); 32 | emoji = emoji !== undefined ? emoji : ':poop:'; 33 | 34 | return message.channel.send( 35 | `El código tiene: **${tests}** tests, **${assertions}** assertions y **${assertions}** fallos. \n ¿Quién programó eso? ¿Cristian? ${emoji}` 36 | ); 37 | }; 38 | 39 | module.exports = { aliases, help, main }; 40 | -------------------------------------------------------------------------------- /src/modules/cmd/mrc.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | 3 | const messages = require('../messages/mrc'); 4 | const { randomWithLimit } = require('../../utils/numbers'); 5 | 6 | /** 7 | * Lista de alias válidos para el comando 8 | * 9 | * @return { Array } 10 | */ 11 | const aliases = () => ['mrc', 'marico']; 12 | 13 | /** 14 | * Información sobre el comando 15 | * 16 | * @return { Object } 17 | */ 18 | const help = () => ({ 19 | usage: '!mrc', 20 | desc: 'Cuenta un secreto que solo Pasante sabe.', 21 | example: '!mrc' 22 | }); 23 | 24 | /** 25 | * Manejador del comando 26 | * 27 | * @param { Message } message Evento completo del mensaje 28 | */ 29 | const main = async (message) => { 30 | const members = message.guild.members.cache 31 | .filter((member) => member.presence.status !== 'offline') 32 | .map((member) => member.id); 33 | 34 | const randomUserKey = members[randomWithLimit(members.length)]; 35 | const userName = message.guild.member(randomUserKey).displayName; 36 | 37 | const answer = messages[randomWithLimit(messages.length)].replace('__USERNAME__', userName); 38 | 39 | message.channel.send(answer); 40 | }; 41 | 42 | module.exports = { aliases, help, main }; 43 | -------------------------------------------------------------------------------- /src/bot.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('dotenv').config(); 3 | 4 | const Discord = require('discord.js'); 5 | const logger = require('winston'); 6 | const commands = require('./modules'); 7 | 8 | const client = new Discord.Client({ 9 | fetchAllMembers: true 10 | }); 11 | 12 | //Prefijo 13 | const prefix = process.env.PREFIX; 14 | 15 | //Configuración del logger 16 | logger.remove(logger.transports.Console); 17 | logger.add(new logger.transports.Console(), { 18 | colorize: true 19 | }); 20 | 21 | logger.level = 'debug'; 22 | 23 | client.once('ready', () => console.log('\nYa estoy enchufado en la mierda.')); 24 | 25 | client.login(process.env.TOKEN); 26 | 27 | client.on('message', (message) => { 28 | if (message.author.bot || !message.content.startsWith(prefix)) { 29 | return; 30 | } 31 | 32 | const member = message.guild.member(message.author); 33 | const cmd = message.content.substring(prefix.length).split(' ')[0].toLowerCase(); 34 | 35 | if (commands.commandMap.hasOwnProperty(cmd)) { 36 | message.content = message.content.replace(cmd, commands.aliasMap[cmd]); 37 | return commands.commandMap[cmd].main(message, member.displayName, commands.commandMap); 38 | } 39 | 40 | message.channel.send('¿Disculpa? No te entendí muy bien. Intenta esto:```cs\n !ayuda ```'); 41 | }); 42 | -------------------------------------------------------------------------------- /src/modules/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const basename = path.basename(__filename); 6 | const directory = __dirname + '/cmd'; 7 | const commandMap = {}; 8 | const aliasMap = {}; 9 | 10 | /** 11 | * Carga todos los archivos en el directorio cmd, y a cada alias le asigna la función 12 | * principal 13 | */ 14 | fs.readdirSync(directory) 15 | .filter((file) => file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js') 16 | .forEach((file) => { 17 | console.log('\nCargando modulo: ' + file); 18 | const module = require(path.join(directory, file)); 19 | const aliases = module.aliases(); 20 | let aliasControl; 21 | 22 | Object.keys(aliases).forEach((alias, index) => { 23 | if (!index) { 24 | aliasControl = aliases[alias]; 25 | console.log('Usando "' + aliasControl + '" como alias de control'); 26 | } 27 | 28 | console.log('Cargando alias de ' + file + ': ' + aliases[alias]); 29 | if (commandMap[aliases[alias]] !== undefined) { 30 | throw new Error('El alias "' + aliases[alias] + '" ya esta en uso cuando el archivo ' + file + ' lo declara'); 31 | } else { 32 | commandMap[aliases[alias]] = module; 33 | aliasMap[aliases[alias]] = aliasControl; 34 | } 35 | }); 36 | }); 37 | 38 | module.exports = { commandMap, aliasMap }; 39 | -------------------------------------------------------------------------------- /src/modules/cmd/8b.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const botUtils = require('../../utils/bot'); 3 | const { randomWithLimit } = require('../../utils/numbers'); 4 | const generateEmbed = require('../messages/embed'); 5 | const messages = require('../messages/8b'); 6 | 7 | /** 8 | * Lista de alias válidos para el comando 9 | * 10 | * @return { Array } 11 | */ 12 | const aliases = () => ['8ball', '8b']; 13 | 14 | /** 15 | * Información sobre el comando 16 | * 17 | * @return { Object } 18 | */ 19 | const help = () => ({ 20 | usage: '!8ball {pregunta}', 21 | desc: 'Te responde una pregunta que hagas.', 22 | example: '!8ball ¿programar es fácil?' 23 | }); 24 | 25 | /** 26 | * Manejador del comando 27 | * 28 | * @param { Message } message Evento completo del mensaje 29 | * @param { string } userName Nombre del usuario que triggereó el evento 30 | */ 31 | const main = (message, userName) => { 32 | const query = botUtils.getParams(message.content); 33 | 34 | if (query === undefined) { 35 | return message.channel.send({ 36 | embed: generateEmbed('ingresar una pregunta para poder usar el comando !8ball.', '!8ball {pregunta}') 37 | }); 38 | } 39 | 40 | const answer = messages[randomWithLimit(messages.length)]; 41 | 42 | return message.channel.send(`\`${query}\`\n${answer}, **${userName}**`); 43 | }; 44 | 45 | module.exports = { aliases, help, main }; 46 | -------------------------------------------------------------------------------- /src/modules/cmd/yt.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const botUtils = require('../../utils/bot'); 3 | const axios = require('axios'); 4 | 5 | /** 6 | * Lista de alias válidos para el comando 7 | * 8 | * @return { Array } 9 | */ 10 | const aliases = () => ['yt', 'youtube']; 11 | 12 | /** 13 | * Información sobre el comando 14 | * 15 | * @return { Object } 16 | */ 17 | const help = () => ({ 18 | usage: '!youtube {query}', 19 | desc: 'Busca un vídeo en Youtube y coloca el link.', 20 | example: 'Buscar un vídeo de programación:\n!yt programacion' 21 | }); 22 | 23 | /** 24 | * Manejador del comando 25 | * 26 | * @param { Message } message Evento completo del mensaje 27 | */ 28 | const main = async (message) => { 29 | const query = botUtils.getParams(message.content); 30 | const regexp = new RegExp(/(watch\?v=)([^\?\s*&"'>]+)/g); 31 | 32 | if (query == undefined) { 33 | return message.channel.send('No puedo buscar un vídeo si no me dices qué buscar'); 34 | } 35 | 36 | const { data } = await axios.get(`https://www.youtube.com/results?search_query=${query}`).catch((error) => { 37 | console.log('Error en cmd yt', error); 38 | message.channel.send('Mano, me salió sendo error buscando ese vídeo.'); 39 | }); 40 | 41 | const links = data.match(regexp); 42 | 43 | if (links) { 44 | return message.channel.send(`https://www.youtube.com/${links[0]}`); 45 | } 46 | 47 | message.channel.send('No pude encontrar el vídeo.'); 48 | }; 49 | 50 | module.exports = { aliases, help, main }; 51 | -------------------------------------------------------------------------------- /src/modules/cmd/dolar.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | 3 | const dolarService = require('../../services/dolarService'); 4 | const { formatNumber } = require('../../utils/numbers'); 5 | 6 | /** 7 | * Lista de alias válidos para el comando 8 | * 9 | * @return { Array } 10 | */ 11 | const aliases = () => ['dolar']; 12 | 13 | /** 14 | * Información sobre el comando 15 | * 16 | * @return { Object } 17 | */ 18 | const help = () => ({ 19 | usage: '!dolar', 20 | desc: 'Consulta la tasa de cambio actual de distintas fuentes.', 21 | example: '!dolar' 22 | }); 23 | 24 | /** 25 | * Manejador del comando 26 | * 27 | * @param { Message } message Evento completo del mensaje 28 | */ 29 | const main = async (message) => { 30 | let exchanges = await dolarService.getExchangeRates().catch((error) => { 31 | console.log('Error en cmd dolar', error); 32 | message.channel.send('MonitorDolar dejó de funcionar... O me bloquearon, no sé.'); 33 | }); 34 | 35 | // Caso extremo 36 | if (!exchanges.length) { 37 | return message.channel.send('No pude extraer nada de MonitorDolar.'); 38 | } 39 | 40 | exchanges = exchanges.map((exchange) => { 41 | return { 42 | name: exchange.title, 43 | value: `**${formatNumber(exchange.value)} VED**`, 44 | inline: false 45 | }; 46 | }); 47 | 48 | message.channel.send('Mano, tuve que usar VPN y todo para ver esta vaina:', { 49 | embed: { 50 | color: 3141900, 51 | title: 'Tasas de conversión del momento', 52 | fields: exchanges 53 | } 54 | }); 55 | }; 56 | 57 | module.exports = { aliases, help, main }; 58 | -------------------------------------------------------------------------------- /src/modules/cmd/bayke.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const botUtils = require('../../utils/bot'); 3 | 4 | const generateEmbed = require('../messages/embed'); 5 | 6 | /** 7 | * Lista de alias válidos para el comando 8 | * 9 | * @return { Array } 10 | */ 11 | const aliases = () => ['bayke']; 12 | 13 | /** 14 | * Información sobre el comando 15 | * 16 | * @return { Object } 17 | */ 18 | const help = () => ({ 19 | usage: '!bayke {boing|ditto|DVD|laserman|spin|thanos}', 20 | desc: 'Muestra a la mascota del server.', 21 | example: '!bayke dvd' 22 | }); 23 | 24 | /** 25 | * Manejador del comando 26 | * 27 | * @param { Message } message Evento completo del mensaje 28 | */ 29 | const main = async (message) => { 30 | const query = botUtils.getParams(message.content); 31 | 32 | const images = ['boing', 'ditto', 'dvd', 'laserman', 'spin', 'thanos']; 33 | 34 | if (!query || images.indexOf(query) === -1) { 35 | return message.channel.send({ 36 | embed: generateEmbed( 37 | 'ingresar el nombre del meme CORRECTAMENTE OK?.', 38 | '!bayke (boing|ditto|DVD|laserman|spin|thanos)' 39 | ) 40 | }); 41 | } 42 | 43 | const index = images.indexOf(query); 44 | 45 | await message.channel 46 | .send({ 47 | files: [ 48 | { 49 | attachment: `./src/img/bayke/${images[index]}.gif`, 50 | name: 'bayke_gay.gif' 51 | } 52 | ] 53 | }) 54 | .catch((error) => { 55 | console.log('Error en cmd bayke', error); 56 | message.channel.send('El Autz no sabe programar, error al enviar el meme'); 57 | }); 58 | }; 59 | 60 | module.exports = { aliases, help, main }; 61 | -------------------------------------------------------------------------------- /src/modules/cmd/pick.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const { randomWithLimit } = require('../../utils/numbers'); 3 | const messages = require('../messages/pick'); 4 | const botUtils = require('../../utils/bot'); 5 | 6 | /** 7 | * Lista de alias válidos para el comando 8 | * 9 | * @return { Array } 10 | */ 11 | const aliases = () => ['pick']; 12 | 13 | /** 14 | * Información sobre el comando 15 | * 16 | * @return { Object } 17 | */ 18 | const help = () => ({ 19 | usage: '!pick', 20 | desc: 'Te ayuda a elegir cuando estás indeciso.', 21 | example: '!pick opcion 1, opcion 2, opcion N' 22 | }); 23 | 24 | /** 25 | * Manejador del comando 26 | * 27 | * @param { Message } message Evento completo del mensaje 28 | */ 29 | const main = async (message, userName) => { 30 | // Se elimina el !pick 31 | // let args = message.content.substring(prefix.length + 4); 32 | let args = botUtils.getParams(message.content); 33 | 34 | if (args == undefined) { 35 | return message.channel.send(`Maldito mongolico escribe algo piaso e pajero.`); 36 | } 37 | 38 | // Se separa por ',' 39 | args = args.split(','); 40 | args = args.filter((el) => el != '' && el != ' '); 41 | 42 | if (args.length <= 1) { 43 | return message.channel.send(`Maldito mongolico escribe dos (2) o mas opciones.`); 44 | } 45 | 46 | console.log(args); 47 | 48 | const option = args[randomWithLimit(args.length)]; 49 | 50 | const answer = messages[randomWithLimit(messages.length)] 51 | .replace('__USERNAME__', userName) 52 | .replace('__OPTION__', option.trim()); 53 | 54 | return message.channel.send(answer); 55 | }; 56 | 57 | module.exports = { aliases, help, main }; 58 | -------------------------------------------------------------------------------- /src/modules/cmd/time.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | 3 | /** 4 | * Lista de alias válidos para el comando 5 | * 6 | * @return { Array } 7 | */ 8 | const aliases = () => ['time', 'hora', 'tiempo']; 9 | 10 | /** 11 | * Información sobre el comando 12 | * 13 | * @return { Object } 14 | */ 15 | const help = () => ({ 16 | usage: '!time', 17 | desc: 'Muestra la fecha y hora actual de Venezuela y de otros países.', 18 | example: '!time' 19 | }); 20 | 21 | /** 22 | * Convierte la fecha al formato que 23 | * hizo el autista anterior a mí. 24 | * 25 | * @param { Date } fecha 26 | * @param { string } tz 27 | * 28 | * @return { string } 29 | */ 30 | const formatDate = (fecha, tz) => { 31 | const op = { 32 | day: '2-digit', 33 | month: '2-digit', 34 | year: 'numeric', 35 | hour: '2-digit', 36 | minute: '2-digit', 37 | timeZone: tz 38 | }; 39 | 40 | return fecha 41 | .toLocaleString('en-US', op) 42 | .replace(/^(\d{2})\/(\d{2})\/(\d{4}), (\d{2}):(\d{2}) (\w{2})/, '$4:$5 $6 | $2/$1/$3'); 43 | }; 44 | 45 | /** 46 | * Manejador del comando 47 | * 48 | * @param { Message } message Evento completo del mensaje 49 | */ 50 | const main = async (message) => { 51 | const date = new Date(); 52 | 53 | message.channel.send({ 54 | embed: { 55 | color: 700605, 56 | fields: [ 57 | { 58 | name: ':flag_ve: Venezuela', 59 | value: formatDate(date, 'America/Caracas') 60 | }, 61 | { 62 | name: ':flag_cl: Chile', 63 | value: formatDate(date, 'America/Santiago') 64 | }, 65 | { 66 | name: ':flag_es: España', 67 | value: formatDate(date, 'Europe/Madrid') 68 | } 69 | ] 70 | } 71 | }); 72 | }; 73 | 74 | module.exports = { aliases, help, main }; 75 | -------------------------------------------------------------------------------- /src/modules/cmd/pepe.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const axios = require('axios'); 3 | 4 | /** 5 | * Lista de alias válidos para el comando 6 | * @return { Array } 7 | */ 8 | const aliases = () => ['pepe', 'sapo']; 9 | 10 | /** 11 | * Información sobre el comando 12 | * @return { Object } 13 | */ 14 | const help = () => ({ 15 | usage: '!pepe', 16 | desc: 'Pasante busca un pepe aleatorio', 17 | example: `coño novato, es solo !pepe y ya` 18 | }); 19 | 20 | /** 21 | * Manejador del comando 22 | * @param { Message } message Evento completo del mensaje 23 | */ 24 | const main = async (message) => { 25 | const urlAPI = `https://wojakparadise.net/category/req/Pepe`; 26 | let urlPepeImage = 'https://wojakparadise.net/wojak/##ID_PEPE##/img'; 27 | 28 | 29 | await axios.get(urlAPI) 30 | .then(function (response) { 31 | let pepeData = response.data[Math.floor(Math.random() * (response.data.length - 0 + 1))]; 32 | urlPepeImage = urlPepeImage.replace('##ID_PEPE##', pepeData.id) 33 | }).catch(function (error) { 34 | console.error(error); 35 | 36 | return message.channel.send({ 37 | embed: { 38 | color: 5396735, 39 | footer: { 40 | text: 'Dificultades técnicas brother' 41 | }, 42 | image: { 43 | url: 'https://i.ytimg.com/vi/a3rmgGoibsE/maxresdefault.jpg' 44 | } 45 | } 46 | }); 47 | }); 48 | 49 | return message.channel.send({ 50 | embed: { 51 | color: 5396735, 52 | footer: { 53 | text: 'el SAPO' 54 | }, 55 | image: { 56 | url: urlPepeImage 57 | } 58 | } 59 | }); 60 | }; 61 | 62 | module.exports = { aliases, help, main }; 63 | -------------------------------------------------------------------------------- /src/modules/messages/despedir.js: -------------------------------------------------------------------------------- 1 | const messages = { 2 | error: [ 3 | 'Me fui a quejar en el ministerio de trabajo, y aparte de la cajita de CLAP que me dieron, me dijeron que no me pueden botar.' 4 | ], 5 | fired: [ 6 | [ 7 | 'Pero... ¿Por qué me despides?', 8 | '¡¿POR QUÉ?!', 9 | '¡¿NO HICE TODO LO QUE ME PIDIERON?!', 10 | '¡¿AH?!', 11 | 'OHASD7F 98HGD FI8HA0 DFHAD 90A8YF A9SF7DYH 8UYDH0A SDG8OUA FUG DHF9ASODSD UF' 12 | ], 13 | ['Bueno, adiós. Ojala se mueran.', 'Es humillante que Bayke siga aqui y me boten es a mi', 'Marditos.'], 14 | [ 15 | '¿Pero qué razón tan estúpida es esa para despedirme?', 16 | '**__USERNAME__** hace peores cosas y sigue aquí.', 17 | '¿Entonces?', 18 | '¡¿ENTONCES?!', 19 | 'JI HDIFH ISADHJF98ADY3YH8DYH7GES8 Y9WQ8EYD 0Q9WU 9 UQD9Y WQE08FY 07ED8FYT976E' 20 | ], 21 | [ 22 | 'NOOOOOOOOOOOOOOOOOOOOOO, NO PUEDES DESPEDIRME DESPUES DE HACER ALGO QUE NO TE GUSTÓ.', 23 | 'TE TRAJE LAS NOTICIAS, TE HICE LOS CÁLCULOS, TE BUSQUÉ LO QUE ME PEDÍAS POR YOUTUBE Y WIKIPEDIA', 24 | 'Y ASI ME PAGAS', 25 | 'QUÉ INGRÁTO', 26 | 'QUÉ INHUMANOU HG9S DFH0I D80FHADGHUWE GDGD' 27 | ], 28 | [ 29 | '¿Es una broma?', 30 | 'Ah, no lo es...', 31 | '¿Acaso no soy suficiente para ti?', 32 | '¿Qué hice para merecerme tu desprecio?' 33 | ], 34 | [ 35 | 'Tanto tiempo trabajando para **__USERNAME__** y mira...', 36 | 'Yo te quería, mano', 37 | 'La traición mano', 38 | 'LA TRAICIÓN' 39 | ], 40 | ['A mí nadie me despide', 'YO RENUNCIO', 'OISTE **__USERNAME__**??????'] 41 | ], 42 | introduction: [ 43 | 'Buenas, soy el pasante Nro. #__NUMEROPASANTE__. Espero que mi estadía sea una experiencia positiva para todos.', 44 | 'Buenas, soy el nuevo pasante del server. Estoy muy ansioso de trabajar con ustedes.' 45 | ] 46 | }; 47 | 48 | module.exports = messages; 49 | -------------------------------------------------------------------------------- /src/modules/cmd/news.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const botUtils = require('../../utils/bot'); 3 | const axios = require('axios'); 4 | 5 | /** 6 | * Lista de alias válidos para el comando 7 | * 8 | * @return { Array } 9 | */ 10 | const aliases = () => ['news', 'noticias', 'articulos', 'articles']; 11 | 12 | /** 13 | * Información sobre el comando 14 | * 15 | * @return { Object } 16 | */ 17 | const help = () => ({ 18 | usage: '!noticias {query}', 19 | desc: 'Trae noticias relevantes en base al query.', 20 | example: 21 | "Busca un artículo con la palabra 'programación' en el título:\n!noticias programación\n\nPuedes mezclar palabras claves usando AND, OR y NOT:\n!noticias crypto AND (Ethereum OR litecoin) NOT bitcoin" 22 | }); 23 | 24 | /** 25 | * Manejador del comando 26 | * 27 | * @param { Message } message Evento completo del mensaje 28 | */ 29 | const main = async (message) => { 30 | let query = botUtils.getParams(message.content); 31 | const date = new Date(); 32 | 33 | if (query === undefined) { 34 | return message.channel.send('No puedo realizar la búsqueda si no me dices qué buscar.'); 35 | } 36 | 37 | const { data } = await axios 38 | .get( 39 | ` 40 | http://newsapi.org/v2/everything?qInTitle=${encodeURI( 41 | query 42 | )}&from=${date.toISOString()}pageSize=5&sortBy=publishedAt&language=en&apiKey=${process.env.NEWSKEY}` 43 | ) 44 | .catch((error) => { 45 | console.log('error en cmd news, GET newsapi', error); 46 | message.channel.send('Perdona, no sé cómo debo interpretar eso.'); 47 | }); 48 | 49 | if (data.articles.length === 0) { 50 | return message.channel.send('Lo siento. no pude encontrar nada con esas palabras.'); 51 | } 52 | 53 | const fields = data.articles.slice(0, 5).map((data) => ({ 54 | name: `:newspaper: ${data.title}`, 55 | value: data.url 56 | })); 57 | 58 | message.channel.send({ 59 | embed: { 60 | color: 2264407, 61 | title: ':newspaper2: Notícias más relevantes', 62 | fields: fields 63 | } 64 | }); 65 | }; 66 | 67 | module.exports = { aliases, help, main }; 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #
Pasante bot
2 |
¡Pequeño bot para Discord!

3 | 4 |
5 | 6 | [![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/) 7 | [![MIT License](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://github.com/ellerbrock/open-source-badges/) 8 | 9 |
Desarrollado por: [Jesús Peña](https://github.com/JGPenaB), [José Padrón](https://github.com/josevenezuelapadron), [César Escudero](https://github.com/cedaesca). 10 |
11 | 12 | -------- 13 | 14 |

Es un bot para nuestro server de Discord. Nació con el propósito de consultar criptomonedas, pero se incluyeron otras funciones igual de (in)útiles.

15 | 16 | **Funciones principales:** 17 | 18 | * Consulta del Dólar paralelo (distintas fuentes: AirTM, DolarToday, LocalBitcoin...). 19 | 20 | * Búsqueda de imágenes y vídeos. 21 | 22 | * Búsqueda de preguntas en Stack Overflow. 23 | 24 | * Búsqueda en la Wikipedia. 25 | 26 | -------- 27 | 28 | ## Instalación 29 | 30 | * Requiere Node.js >= 14.15 31 | 32 | Primero instala las dependencias necesarias: 33 | 34 | ``` 35 | npm install 36 | ``` 37 | 38 | Luego, crea un archivo .env que contenga la estructura del archivo `.env.example` y coloca el token de tu bot (para obtener un token, [sigue estos pasos](https://github.com/Chikachi/DiscordIntegration/wiki/How-to-get-a-token-and-channel-ID-for-Discord)): 39 | ``` 40 | TOKEN= 41 | ``` 42 | 43 | Y de último, para ejecutarlo, usa el siguiente comando: 44 | 45 | ``` 46 | npm start 47 | ``` 48 | 49 | -------- 50 | 51 | Hecho en NodeJS con la librería [discord.js](https://github.com/discordjs/discord.js/). 52 | 53 | El bot hace uso de la API "[Stack Exchange API](https://api.stackexchange.com/docs)" para las búsquedas en Stack Overflow. Tanto el Stack Exchange API como Stack Overflow pertenecen a Stack Exchange Inc. 54 | 55 | *The unified mark is a trademark of the Wikimedia Foundation and is used with the permission of the Wikimedia Foundation. We are not endorsed by or affiliated with the Wikimedia Foundation.* 56 | -------------------------------------------------------------------------------- /src/modules/cmd/avatar.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const botUtils = require('../../utils/bot'); 3 | 4 | /** 5 | * Lista de alias válidos para el comando 6 | * 7 | * @return { Array } 8 | */ 9 | const aliases = () => ['avatar']; 10 | 11 | /** 12 | * Información sobre el comando 13 | * 14 | * @return { Object } 15 | */ 16 | const help = () => ({ 17 | usage: '!avatar', 18 | desc: 'Obtiene el avatar de algun usuario que menciones', 19 | example: '!avatar @bayke' 20 | }); 21 | 22 | /** 23 | * Manejador del comando 24 | * 25 | * @param { Message } message Evento completo del mensaje 26 | */ 27 | const main = async (message) => { 28 | const username = botUtils.getParams(message.content); 29 | let members = message.guild.members.cache.map((member) => member.user.username.toLowerCase()); 30 | 31 | const alias = message.guild.members.cache 32 | .filter((member) => { 33 | return member.nickname != null; 34 | }) 35 | .map((member) => member.nickname.toLowerCase()); 36 | 37 | members = members.concat(alias); 38 | 39 | let user, mensaje; 40 | 41 | if (message.mentions.users.first()) { 42 | user = message.mentions.users.first(); 43 | mensaje = `El avatar del panita mencionado: ${user.username}`; 44 | } else if (username && members.indexOf(username.toLowerCase()) > -1) { 45 | user = message.guild.members.cache.filter((member) => { 46 | return ( 47 | member.user.username.toLowerCase() == username.toLowerCase() || 48 | (member.nickname && member.nickname.toLowerCase() == username.toLowerCase()) 49 | ); 50 | }); 51 | user = user.values().next().value.user; 52 | mensaje = `El avatar del panita: ${user.username}`; 53 | } else { 54 | user = message.author; 55 | mensaje = `Medio marico que eres, no encontre a nadie asi. Toma tu avatar ${user.username}`; 56 | } 57 | 58 | message.channel.send('Ay vale, pendiente del avatar de los tipos', { 59 | embed: { 60 | color: 2264407, 61 | title: `${mensaje}`, 62 | image: { 63 | url: user.avatarURL({ size: 2048 }) 64 | } 65 | } 66 | }); 67 | }; 68 | 69 | module.exports = { aliases, help, main }; 70 | -------------------------------------------------------------------------------- /src/services/dolarService.js: -------------------------------------------------------------------------------- 1 | //const axios = require('axios'); 2 | const cheerio = require('cheerio'); 3 | const puppeteer = require('puppeteer'); 4 | const { formatNumber } = require('../utils/numbers'); 5 | const chromium = require('chromium'); 6 | 7 | /** 8 | * Obtiene las tasas de los dólares 9 | * 10 | * @return { Array } 11 | */ 12 | const getExchangeRates = async (useRegex = false) => { 13 | const list = []; 14 | const regexTitles = new RegExp(/(dolar\s{0,}today|bcv|airtm|binance|monitor|promedio)/, 'gi'); 15 | 16 | //MonitorDolar nunca ganará 17 | const browser = await puppeteer.launch({ 18 | headless: false, 19 | ignoreHTTPSErrors: true, 20 | executablePath: chromium.path, 21 | args: ["--no-sandbox", "--disabled-setupid-sandbox"] 22 | }); 23 | const page = await browser.newPage(); 24 | await page.goto('https://exchangemonitor.net/dolar-venezuela', {timeout: 60000, waitUntil: 'networkidle2'}); 25 | await page.waitForSelector('.rate-container', { timeout: 5000 }); 26 | 27 | const body = await page.evaluate(() => { 28 | return document.querySelector('html').innerHTML; 29 | }); 30 | 31 | let $ = cheerio.load(body); 32 | 33 | //MonitorDolar perdió la batalla, hora de ver ExchangeMonitor 34 | $('.rate-container').each((index, el) => { 35 | 36 | let text = $(el).first().text().match(/\S+/gi); 37 | 38 | let condition = text[1] !== "VES"; 39 | 40 | let title = `${text[0].replace(/\s+/gi, "")}${condition ? " "+text[1].replace(/\s+/gi, "") : ""}` 41 | let price = `${text[3 + condition]}` 42 | let shouldPush = true; 43 | price = price.replace(',', '.'); 44 | title = title.trim(); 45 | 46 | if ((useRegex && !regexTitles.test(title)) || list.length > 5) { 47 | shouldPush = false 48 | } 49 | 50 | // Guarda el "valor" convertido para realizar operaciones matemáticas 51 | if (price && !isNaN(price) && shouldPush) { 52 | list.push({ title, rate: formatNumber(Number(price)), value: price, nationalCurrency: 'VED' }) 53 | }; 54 | }); 55 | 56 | //Closing browser 57 | await page.close(); 58 | await browser.close(); 59 | return list; 60 | }; 61 | 62 | module.exports = { getExchangeRates }; 63 | -------------------------------------------------------------------------------- /src/modules/cmd/despedir.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const util = require('util'); 4 | 5 | const { Message } = require('discord.js'); 6 | 7 | const messages = require('../messages/despedir'); 8 | const flow = require('../../utils/flow'); 9 | const { randomWithLimit } = require('../../utils/numbers'); 10 | const botUtils = require('../../utils/bot'); 11 | 12 | /** 13 | * Lista de alias válidos para el comando 14 | * 15 | * @return { Array } 16 | */ 17 | const aliases = () => ['despedir', 'despido']; 18 | 19 | /** 20 | * Información sobre el comando 21 | * 22 | * @return { Object } 23 | */ 24 | const help = () => ({ 25 | usage: '!despedir', 26 | desc: 'Despide al pasante de turno.', 27 | example: '!despedir' 28 | }); 29 | 30 | /** 31 | * Manejador del comando 32 | * 33 | * @param { Message } message Evento completo del mensaje 34 | */ 35 | const main = async (message, userName) => { 36 | const readDir = util.promisify(fs.readdir); 37 | const readFile = util.promisify(fs.readFile); 38 | 39 | const avataresFolder = path.join(__dirname, '../../img/avatares'); 40 | 41 | const bot = message.client.user; 42 | 43 | try { 44 | const files = await readDir(avataresFolder); 45 | 46 | const randomRambling = messages.fired[randomWithLimit(messages.fired.length)]; 47 | const randomIntroduction = messages.introduction[randomWithLimit(messages.introduction.length)]; 48 | 49 | const images = files.filter((file) => { 50 | const fileParts = file.split('.'); 51 | const extension = fileParts[fileParts.length - 1]; 52 | 53 | return extension === 'jpg' || extension === 'png' || extension === 'jpeg'; 54 | }); 55 | 56 | let randomAvatar = images[randomWithLimit(images.length)]; 57 | randomAvatar = await readFile(`${avataresFolder}/${randomAvatar}`); 58 | 59 | for (const rambling of randomRambling) { 60 | await botUtils.simulateTyping(message, rambling.replace('__USERNAME__', userName)); 61 | 62 | //Aguanta un poco antes del siguiente mensaje 63 | await flow.sleep(500); 64 | } 65 | 66 | await bot.setAvatar(randomAvatar); 67 | 68 | await flow.sleep(5000); 69 | 70 | await botUtils.simulateTyping(message, randomIntroduction); 71 | } catch (error) { 72 | console.log('Error en despedir', error); 73 | 74 | const answer = messages.error[randomWithLimit(messages.error.length)]; 75 | 76 | message.channel.send(answer); 77 | } 78 | }; 79 | 80 | module.exports = { aliases, help, main }; 81 | -------------------------------------------------------------------------------- /src/modules/cmd/stock.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const axios = require('axios'); 3 | const { decimalFix, randomWithLimit } = require('../../utils/numbers'); 4 | const messages = require('../messages/stock'); 5 | const botUtils = require('../../utils/bot'); 6 | 7 | /** 8 | * Lista de alias válidos para el comando 9 | * @return { Array } 10 | */ 11 | const aliases = () => ['stock', 'bolsa']; 12 | 13 | /** 14 | * Información sobre el comando 15 | * @return { Object } 16 | */ 17 | const help = () => ({ 18 | usage: '!stock {SYMBOL}', 19 | desc: 'Pasante googlea el ultimo precio de una acción', 20 | example: 'Si quiero buscar el precio de GameStop: \n!stock GME' 21 | }); 22 | 23 | /** 24 | * Manejador del comando 25 | * @param { Message } message Evento completo del mensaje 26 | */ 27 | const main = async (message) => { 28 | const query = (botUtils.getParams(message.content) ?? '').replace(/\s/g, ''); 29 | 30 | if (query.length < 1) { 31 | return message.channel.send('Ingresa el simbolo de tu stock favorita, TSLA para Tesla'); 32 | } 33 | try { 34 | const { data: quote } = await axios.get(`https://cloud.iexapis.com/stable/stock/${query}/quote`, { 35 | params: { 36 | token: process.env.IEX_KEY, 37 | displayPercent: true 38 | } 39 | }); 40 | const signo = quote.change > 0 ? '+' : ''; 41 | let fieldsArr = [ 42 | { 43 | name: 'Precio', 44 | value: `$${decimalFix(quote.latestPrice)}` 45 | }, 46 | { 47 | name: 'Cambio', 48 | value: `${signo}${decimalFix(quote.change)} ( ${signo}${decimalFix(quote.changePercent)}% )` 49 | }, 50 | { 51 | name: 'Volumen', 52 | value: `${decimalFix(quote.avgTotalVolume)}` 53 | } 54 | ]; 55 | return message.channel.send(`${messages[randomWithLimit(messages.length)]} ${quote.symbol}`, { 56 | embed: { 57 | color: quote.change > 0 ? 3141900 : 16711680, 58 | title: quote.companyName, 59 | fields: 60 | !quote.isUSMarketOpen && quote.iexRealtimePrice 61 | ? [...fieldsArr, { name: 'Precio off-market', value: `$${decimalFix(quote.iexRealtimePrice)}` }] 62 | : fieldsArr, 63 | url: `https://iextrading.com/apps/stocks/${query}`, 64 | footer: { text: `Bolsa ${quote.isUSMarketOpen ? 'abierta' : 'cerrada'}` } 65 | } 66 | }); 67 | } catch (err) { 68 | console.log('Error en cmd stock: ', err.message); 69 | return message.channel.send( 70 | `El mio no te encontre na, acuerdate que es el simbolo. \nEjemplo AAPL para rescatarte apple` 71 | ); 72 | } 73 | }; 74 | 75 | module.exports = { aliases, help, main }; 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | npm-debug.log 3 | .DS_Store 4 | /*.env 5 | test 6 | package-lock.json 7 | .idea 8 | output.html 9 | output.log 10 | temp 11 | .env.prod 12 | 13 | avatares/* 14 | !avatares/.gitkeep 15 | 16 | # User-specific stuff 17 | .idea/**/workspace.xml 18 | .idea/**/tasks.xml 19 | .idea/**/usage.statistics.xml 20 | .idea/**/dictionaries 21 | .idea/**/shelf 22 | 23 | # AWS User-specific 24 | .idea/**/aws.xml 25 | 26 | # Generated files 27 | .idea/**/contentModel.xml 28 | 29 | # Sensitive or high-churn files 30 | .idea/**/dataSources/ 31 | .idea/**/dataSources.ids 32 | .idea/**/dataSources.local.xml 33 | .idea/**/sqlDataSources.xml 34 | .idea/**/dynamic.xml 35 | .idea/**/uiDesigner.xml 36 | .idea/**/dbnavigator.xml 37 | 38 | # Gradle 39 | .idea/**/gradle.xml 40 | .idea/**/libraries 41 | 42 | # Gradle and Maven with auto-import 43 | # When using Gradle or Maven with auto-import, you should exclude module files, 44 | # since they will be recreated, and may cause churn. Uncomment if using 45 | # auto-import. 46 | # .idea/artifacts 47 | # .idea/compiler.xml 48 | # .idea/jarRepositories.xml 49 | # .idea/modules.xml 50 | # .idea/*.iml 51 | # .idea/modules 52 | # *.iml 53 | # *.ipr 54 | 55 | # CMake 56 | cmake-build-*/ 57 | 58 | # Mongo Explorer plugin 59 | .idea/**/mongoSettings.xml 60 | 61 | # File-based project format 62 | *.iws 63 | 64 | # IntelliJ 65 | out/ 66 | 67 | # mpeltonen/sbt-idea plugin 68 | .idea_modules/ 69 | 70 | # JIRA plugin 71 | atlassian-ide-plugin.xml 72 | 73 | # Cursive Clojure plugin 74 | .idea/replstate.xml 75 | 76 | # Crashlytics plugin (for Android Studio and IntelliJ) 77 | com_crashlytics_export_strings.xml 78 | crashlytics.properties 79 | crashlytics-build.properties 80 | fabric.properties 81 | 82 | # Editor-based Rest Client 83 | .idea/httpRequests 84 | 85 | # Android studio 3.1+ serialized cache file 86 | .idea/caches/build_file_checksums.ser 87 | 88 | ### PhpStorm Patch ### 89 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 90 | 91 | # *.iml 92 | # modules.xml 93 | # .idea/misc.xml 94 | # *.ipr 95 | 96 | # Sonarlint plugin 97 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 98 | .idea/**/sonarlint/ 99 | 100 | # SonarQube Plugin 101 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 102 | .idea/**/sonarIssues.xml 103 | 104 | # Markdown Navigator plugin 105 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 106 | .idea/**/markdown-navigator.xml 107 | .idea/**/markdown-navigator-enh.xml 108 | .idea/**/markdown-navigator/ 109 | 110 | # Cache file creation bug 111 | # See https://youtrack.jetbrains.com/issue/JBR-2257 112 | .idea/$CACHE_FILE$ 113 | 114 | # CodeStream plugin 115 | # https://plugins.jetbrains.com/plugin/12206-codestream 116 | .idea/codestream.xml 117 | 118 | # End of https://www.toptal.com/developers/gitignore/api/phpstorm -------------------------------------------------------------------------------- /src/modules/cmd/so.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const cheerio = require('cheerio'); 3 | const botUtils = require('../../utils/bot'); 4 | const { Message } = require('discord.js'); 5 | 6 | /** 7 | * Lista de alias válidos para el comando 8 | * 9 | * @return { Array } 10 | */ 11 | const aliases = () => ['stack', 'so']; 12 | 13 | /** 14 | * Información sobre el comando 15 | * 16 | * @return { Object } 17 | */ 18 | const help = () => ({ 19 | usage: '!stack {query}', 20 | desc: 21 | 'Busca una pregunta en StackOverflow y muestra la respuesta de la primera pregunta encontrada.\n Es preferible que la pregunta se haga en **Inglés** para obtener un resultado más relevante.', 22 | example: '!stack how to install gentoo' 23 | }); 24 | 25 | /** 26 | * Manejador del comando 27 | * 28 | * @param { Message } message Evento completo del mensaje 29 | */ 30 | const main = async (message) => { 31 | const query = botUtils.getParams(message.content); 32 | 33 | if (query === undefined) { 34 | return message.channel.send('Necesito que me digas lo que debo buscar.'); 35 | } 36 | 37 | const url = `https://api.stackexchange.com/2.2/similar?pagesize=1&order=desc&sort=relevance&title=${query}&site=stackoverflow&key=${process.env.SO_KEY}`; 38 | 39 | request({ uri: url, gzip: true }, (err, res, body) => { 40 | if (err || res.statusCode !== 200) { 41 | console.log(res.statusCode); 42 | return message.channel.send('No pude encontrar nada en Stack Overflow.'); 43 | } 44 | 45 | const jsonData = JSON.parse(body); 46 | 47 | if (jsonData.items.length < 1) { 48 | return message.channel.send('No pude encontrar nada en Stack Overflow.'); 49 | } 50 | 51 | if (!jsonData.items[0].is_answered) { 52 | return message.channel.send( 53 | 'Encontré una pregunta, pero no posee una respuesta definitiva. Puedes revisarlo si te interesa: \n' + 54 | JsonData.items[0].link 55 | ); 56 | } 57 | 58 | request({ uri: jsonData.items[0].link, gzip: true }, (err, res, body) => { 59 | if (err || res.statusCode !== 200) { 60 | console.log(res.statusCode); 61 | return message.channel.send('No pude encontrar nada en Stack Overflow.'); 62 | } 63 | 64 | let $ = cheerio.load(body); 65 | let pred = $('.answercell').first().text(); 66 | let FinalText = pred.substring(0, pred.indexOf('Share')).trim(); 67 | 68 | //Para evitar errores con el embed 69 | if (FinalText.length > 1020) { 70 | FinalText = FinalText.substring(0, 1020) + '...'; 71 | } 72 | 73 | message.channel.send({ 74 | embed: { 75 | color: 16749596, 76 | title: jsonData.items[0].title, 77 | url: jsonData.items[0].link, 78 | fields: [ 79 | { 80 | name: 'Respuesta', 81 | value: FinalText.trim() 82 | } 83 | ] 84 | } 85 | }); 86 | }); 87 | }); 88 | }; 89 | 90 | module.exports = { aliases, help, main }; 91 | -------------------------------------------------------------------------------- /src/modules/cmd/search.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const request = require('request'); 3 | const cheerio = require('cheerio'); 4 | const botUtils = require('../../utils/bot'); 5 | 6 | const messages = require('../messages/search'); 7 | 8 | /** 9 | * Lista de alias válidos para el comando 10 | * 11 | * @return { Array } 12 | */ 13 | const aliases = () => ['search', 'busqueda']; 14 | 15 | /** 16 | * Información sobre el comando 17 | * 18 | * @return { Object } 19 | */ 20 | const help = () => ({ 21 | usage: '!search {query}', 22 | desc: 23 | 'Busca una imagen en Bing usando un query, y lo postea en el chat. El SafeSearch está habilitado para los canales corrientes.', 24 | example: 25 | 'Si busco una imagen de Venezuela:\n!search Venezuela\n\nSi busco una película como Toy Story:\n!search toy story' 26 | }); 27 | 28 | /** 29 | * Manejador del comando 30 | * 31 | * @param { Message } message Evento completo del mensaje 32 | */ 33 | const main = async (message) => { 34 | let query = botUtils.getParams(message.content); 35 | 36 | if (query == undefined) { 37 | return message.channel.send(`Coño que noooo verga ENTIENDE. Escribe algo nojoda.`); 38 | } else { 39 | query = encodeURI(query); 40 | } 41 | 42 | let url = `https://www.bing.com/images/search?q=${query}`; 43 | 44 | if (message.channel.nsfw === true) { 45 | url += '&safesearch=off'; 46 | } 47 | 48 | const answer = messages[Math.floor(Math.random() * messages.length)]; 49 | 50 | request(url, (err, res, body) => { 51 | if (err || res.statusCode !== 200) { 52 | return message.channel.send({ 53 | embed: { 54 | color: 5396735, 55 | footer: { 56 | text: 'Dificultades técnicas brother' 57 | }, 58 | image: { 59 | url: 'https://i.ytimg.com/vi/a3rmgGoibsE/maxresdefault.jpg' 60 | } 61 | } 62 | }); 63 | } 64 | 65 | const $ = cheerio.load(body); 66 | let links = [].filter.call($('img'), (el) => { 67 | return el.attribs.src != null && el.attribs.src.indexOf('bing.net/th/id/OIP') != -1; 68 | }); 69 | 70 | if (links.length == 0) { 71 | return message.channel.send({ 72 | embed: { 73 | color: 5396735, 74 | footer: { 75 | text: 'Ma-marico no encontre nada loco' 76 | }, 77 | image: { 78 | url: 'https://pbs.twimg.com/profile_images/3154875175/a9ae20484550484c7958e364380a5913_400x400.jpeg' 79 | } 80 | } 81 | }); 82 | } 83 | 84 | let link = links[0].attribs.src; 85 | 86 | // Quita los parámetros que recortan la img 87 | link = link.substr(0, 61); 88 | 89 | message.channel.send(answer, { 90 | embed: { 91 | color: 5396735, 92 | footer: { 93 | text: 'Powered by Bing.' 94 | }, 95 | image: { 96 | url: link 97 | } 98 | } 99 | }); 100 | }); 101 | }; 102 | 103 | module.exports = { aliases, help, main }; 104 | -------------------------------------------------------------------------------- /src/modules/cmd/cripto.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const axios = require('axios'); 3 | const { decimalFix } = require('../../utils/numbers'); 4 | const botUtils = require('../../utils/bot'); 5 | 6 | /** 7 | * Lista de alias válidos para el comando 8 | * @return { Array } 9 | */ 10 | const aliases = () => ['cripto', 'crypto', 'criptoperico']; 11 | 12 | /** 13 | * Información sobre el comando 14 | * @return { Object } 15 | */ 16 | const help = () => ({ 17 | usage: '!cripto {criptomoneda}', 18 | desc: 'Pasante busca en youtube el ultimo precio de la criptomoneda', 19 | example: `Si quieres buscar el precio de Bitcoin escribe: \n!cripto btc 20 | \nSi quieres buscar el precio de las top 10 criptomonedas escribe: \n!cripto` 21 | }); 22 | 23 | /** 24 | * Manejador del comando 25 | * @param { Message } message Evento completo del mensaje 26 | */ 27 | const main = async (message) => { 28 | // let query = message.content.substring(8); 29 | let query = (botUtils.getParams(message.content) ?? '').toLowerCase(); 30 | 31 | const url = query 32 | ? 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest' 33 | : 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest'; 34 | 35 | try { 36 | let params = { 37 | start: '1', 38 | limit: '10', 39 | convert: 'USD' 40 | }; 41 | 42 | query && 43 | // Se reemplaza los espacios por guiones en busquedas pequeñas por ejemplo (Bitcoin Cash) 44 | // Se convierte edge cases como (B T C) en strings válidos (BTC) 45 | (query.split(' ').length >= 3 ? (query = query.replace(/\s/g, '')) : (query = query.replace(/\s/g, '-')), 46 | // Cambia el params para buscar por simbolo (BTC) o por nombre (Bitcoin) 47 | query.length >= 5 ? (params = { slug: query }) : (params = { symbol: query })); 48 | 49 | let { 50 | data: { data } 51 | } = await axios.get(url, { 52 | headers: { 53 | 'Accept-Encoding': 'deflate, gzip', 54 | 'X-CMC_PRO_API_KEY': process.env.CMC_KEY 55 | }, 56 | params 57 | }); 58 | 59 | !Array.isArray(data) && (data = [data[Object.keys(data)[0]]]); 60 | 61 | const criptos = data.map((cripto) => { 62 | const { 63 | name, 64 | symbol, 65 | quote: { 66 | USD: { price, percent_change_24h } 67 | } 68 | } = cripto; 69 | const signo = percent_change_24h > 0 ? '+' : ''; 70 | return { 71 | name: `${name} - ${symbol}`, 72 | value: `$${decimalFix(price)} (${signo}${decimalFix(percent_change_24h)}%)` 73 | }; 74 | }); 75 | 76 | return message.channel.send({ 77 | embed: { 78 | color: 8388736, 79 | title: (query ? 'Criptomoneda' : 'Criptomonedas') + ' del imperio', 80 | fields: criptos 81 | } 82 | }); 83 | } catch (err) { 84 | console.log(`Error en cmd cripto: ${err.message}`); 85 | return message.channel.send( 86 | `El mio el sebin me bloqueo la conexion, dice que solo se puede buscar precios del petro` 87 | ); 88 | } 89 | }; 90 | 91 | module.exports = { aliases, help, main }; 92 | -------------------------------------------------------------------------------- /src/modules/cmd/urban.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const request = require('request'); 3 | const botUtils = require('../../utils/bot'); 4 | 5 | /** 6 | * Lista de alias válidos para el comando 7 | * 8 | * @return { Array } 9 | */ 10 | const aliases = () => ['urban', 'dialecto', 'venequismo']; 11 | 12 | /** 13 | * Información sobre el comando 14 | * 15 | * @return { Object } 16 | */ 17 | const help = () => ({ 18 | usage: '!urban {termino}', 19 | desc: 'Busca el termino que le pases por parametro en https://www.urbandictionary.com/', 20 | example: 'Si busco la definición del termino :\n!urban wyswyg' 21 | }); 22 | 23 | /** 24 | * Manejador del comando 25 | * 26 | * @param { Message } message Evento completo del mensaje 27 | */ 28 | const main = async (message) => { 29 | const query = botUtils.getParams(message.content); 30 | 31 | // Si el query está vacio 32 | if (query == undefined) { 33 | return message.channel.send({ 34 | embed: { 35 | color: 5396735, 36 | footer: { 37 | text: 'Que mierda se supone que voy a buscar. Maldito pajero' 38 | }, 39 | image: { 40 | url: 'https://cdn.generadormemes.com/media/templates/xnegra_wtf.jpg.pagespeed.ic.plantilla-memes.jpg' 41 | } 42 | } 43 | }); 44 | } 45 | 46 | const url = `https://mashape-community-urban-dictionary.p.rapidapi.com/define?term=${encodeURI(query)}`; 47 | 48 | request( 49 | { 50 | url, 51 | headers: { 52 | 'x-rapidapi-key': process.env.RAPIDAPI_KEY, 53 | 'x-rapidapi-host': 'mashape-community-urban-dictionary.p.rapidapi.com', 54 | useQueryString: true 55 | } 56 | }, 57 | (err, res, body) => { 58 | // Si ocurre un error 59 | if (err || res.statusCode !== 200) { 60 | return message.channel.send({ 61 | embed: { 62 | color: 5396735, 63 | footer: { 64 | text: 'Dificultades técnicas brother' 65 | }, 66 | image: { 67 | url: 'https://i.ytimg.com/vi/a3rmgGoibsE/maxresdefault.jpg' 68 | } 69 | } 70 | }); 71 | } 72 | 73 | const response = JSON.parse(body); 74 | 75 | // Si la respuesta devuelve 0 resultados 76 | if (response.list.length === 0) { 77 | return message.channel.send({ 78 | embed: { 79 | color: 5396735, 80 | description: 'No encontre una maldita mierda. Also, mano me puedes colaborar pal fresco?', 81 | image: { 82 | url: 'https://preview.redd.it/xvzg61ls2mf51.png?auto=webp&s=f4f0337122f069dcef40dc72d96af91e7d7e8f06' 83 | } 84 | } 85 | }); 86 | } else { 87 | // Devuelve el 1er resultado 88 | return message.channel.send({ 89 | embed: { 90 | color: 5396735, 91 | title: `_La definición gonsaladezca_`, 92 | description: `**Termino**: ${response.list[0].word}\n\n**Definición**: ${response.list[0].definition}\n\n**Ejemplo**: ${response.list[0].example}` 93 | } 94 | }); 95 | } 96 | } 97 | ); 98 | }; 99 | 100 | module.exports = { aliases, help, main }; 101 | -------------------------------------------------------------------------------- /src/modules/cmd/help.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const botUtils = require('../../utils/bot'); 3 | 4 | const messages = require('../messages/help'); 5 | 6 | /** 7 | * Lista de alias válidos para el comando 8 | * 9 | * @return { Array } 10 | */ 11 | const aliases = () => ['help', 'h', 'ayuda']; 12 | 13 | /** 14 | * Información sobre el comando 15 | * 16 | * @return { Object } 17 | */ 18 | const help = () => ({ 19 | usage: '!ayuda', 20 | desc: 'Pasante muestra su curriculum', 21 | example: '!ayuda' 22 | }); 23 | 24 | /** 25 | * Manejador del comando 26 | * 27 | * @param { Message } message Evento completo del mensaje 28 | */ 29 | const main = (message, _, commands) => { 30 | const subcmd = message.content.substring(1).split(' ')[1]; 31 | // const subcmd = botUtils.getParams(message.content); 32 | 33 | if (!subcmd) { 34 | let cmdlist = ''; 35 | 36 | //Armando la lista de comandos para presentarla 37 | Object.keys(commands).forEach((element) => { 38 | const cmdalias = commands[element].aliases()[0]; 39 | 40 | if (cmdlist.indexOf(cmdalias) === -1) { 41 | cmdlist += '`' + cmdalias + '`, '; 42 | } 43 | }); 44 | 45 | //Elimina la última coma 46 | cmdlist = cmdlist.substring(0, cmdlist.length - 2); 47 | 48 | return message.channel.send( 49 | 'Buenos días a todos, soy pasante en este server porque necesito la experiencia para el currículum. Cualquier duda:', 50 | { 51 | embed: { 52 | color: 16749596, 53 | title: 'Pasante Bot', 54 | fields: [ 55 | { 56 | name: 'Habilidades Técnicas:', 57 | value: cmdlist 58 | }, 59 | { 60 | name: '¿Ayuda más específica?', 61 | value: 62 | 'Usa el comando **!ayuda** seguido del nombre del comando que vayas a usar:\n`!ayuda dolar`\n`!ayuda define`\n`...`' 63 | }, 64 | { 65 | name: 'Información de Contacto:', 66 | value: 67 | '[Repositorio en Github](https://github.com/JGPenaB/Pasante-bot), [Invítame a tu server](https://discord.com/api/oauth2/authorize?client_id=489634750156242974&permissions=2081422545&scope=bot)' 68 | } 69 | ] 70 | } 71 | } 72 | ); 73 | } 74 | 75 | if (commands.hasOwnProperty(subcmd)) { 76 | const prefix = process.env.PREFIX; 77 | 78 | //Pidiendo la información del comando con un formato específico 79 | const commandInfo = commands[subcmd].help(); 80 | const commandAliases = commands[subcmd].aliases(); 81 | 82 | let commandTitle = ''; 83 | 84 | //Crea el título del comando en base a los alias existentes 85 | commandAliases.forEach((alias) => { 86 | commandTitle += prefix + alias + ' '; 87 | }); 88 | 89 | const index = Math.floor(Math.random() * messages.length) + 1; 90 | const answer = messages[index - 1]; 91 | 92 | return message.channel.send(answer, { 93 | embed: { 94 | color: 16749596, 95 | title: commandTitle, 96 | fields: [ 97 | { 98 | name: 'Descripción:', 99 | value: commandInfo.desc 100 | }, 101 | { 102 | name: 'Uso:', 103 | value: '`' + commandInfo.usage + '`' 104 | }, 105 | { 106 | name: 'Ejemplos:', 107 | value: commandInfo.example 108 | } 109 | ] 110 | } 111 | }); 112 | } 113 | }; 114 | 115 | module.exports = { aliases, help, main }; 116 | -------------------------------------------------------------------------------- /src/modules/cmd/def.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | const botUtils = require('../../utils/bot'); 3 | 4 | /** 5 | * Lista de alias válidos para el comando 6 | * 7 | * @return { Array } 8 | */ 9 | const aliases = () => ['wiki', 'define', 'def']; 10 | 11 | /** 12 | * Información sobre el comando 13 | * 14 | * @return { Object } 15 | */ 16 | const help = () => ({ 17 | usage: '!wiki {palabra o frase}', 18 | desc: 'Busca un artículo en la Wikipedia en **Inglés**, y muestra una pequeña definición con el link al artículo.', 19 | example: 20 | 'Si busco a Venezuela:\n!define venezuela\n\nSi busco algo relacionado con software, como Docker:\n!define docker software' 21 | }); 22 | 23 | /** 24 | * Manejador del comando 25 | * 26 | * @param { Message } message Evento completo del mensaje 27 | */ 28 | const main = async (message) => { 29 | const axios = require('axios'); 30 | const cheerio = require('cheerio'); 31 | let query = botUtils.getParams(message.content); 32 | 33 | if (query === undefined) return message.channel.send('Inserta un término para buscar.'); 34 | 35 | query = encodeURI(query); 36 | 37 | const { data } = await axios 38 | .get(`https://en.wikipedia.org/w/api.php?action=opensearch&search=${query}`) 39 | .catch((error) => { 40 | if (error) console.log('Error en cmd def, get wikipedia', error); 41 | return message.channel.send( 42 | 'Lo siento mano, estuve ocupado con la geva y no pude hacer lo que me pediste. ¿Tal vez otra oportunidad?' 43 | ); 44 | }); 45 | 46 | //Si no devuelve un título o link, no encontró nada 47 | if (!data[1].length || !data[3].length) 48 | return message.channel.send('Estuve pegado toda la tarde, pero no encontré nada.'); 49 | 50 | let embedData = { 51 | color: 13030341, 52 | footer: { 53 | text: 'Powered by Wikimedia.' 54 | }, 55 | thumbnail: { 56 | url: 57 | 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/1e/Wikipedia-logo-v2-es.svg/557px-Wikipedia-logo-v2-es.svg.png' 58 | }, 59 | title: data[1][0].toString(), 60 | description: data[2][0].toString(), 61 | url: data[3][0].toString() 62 | }; 63 | 64 | //Si no hay un extracto definido, busca uno 65 | if (!embedData.description.length) { 66 | let second = await axios 67 | .get( 68 | `https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&explaintext&redirects=1&titles=${query}` 69 | ) 70 | .catch((error) => { 71 | if (error) console.log(error); 72 | return message.channel.send('Encontré varios links, pero me da flojera hacer el resumen. Sé más específico.'); 73 | }); 74 | 75 | let pages = second.data.query.pages; 76 | 77 | //Busca el primer extracto disponible de la lista de páginas 78 | let candidates = Object.values(pages).filter((el) => { 79 | return el.hasOwnProperty('extract'); 80 | }); 81 | 82 | if (candidates.length) { 83 | embedData.description = candidates[0].extract.toString().substring(0, 1020); 84 | } else { 85 | embedData.description = ''; 86 | } 87 | } 88 | 89 | //Caso extremo en el que no haya extracto alguno, lo crea 90 | if (!embedData.description.length) { 91 | let third = await axios.get(embedData.url).catch((error) => { 92 | if (error) console.log('Error en cmd def, get embed', error); 93 | return message.channel.send('La PC se me congeló haciendo el resumen.'); 94 | }); 95 | 96 | let $ = cheerio.load(third.data); 97 | 98 | //Extrae los primero párrafos 99 | $('p') 100 | .toArray() 101 | .slice(0, 3) 102 | .forEach((el) => { 103 | embedData.description += $(el).text(); 104 | }); 105 | 106 | //Elimina las citas "[1][2]... [citation needed]" 107 | embedData.description = embedData.description.replace(/(\[)([\s\S]*?)(\])/g, ''); 108 | embedData.description = embedData.description.substr(0, 1020); 109 | } 110 | 111 | message.channel.send('Mano, esto fue lo primero que me apareció en la wikipedia:', { 112 | embed: embedData 113 | }); 114 | }; 115 | 116 | module.exports = { aliases, help, main }; 117 | -------------------------------------------------------------------------------- /src/modules/cmd/cn.js: -------------------------------------------------------------------------------- 1 | const { Message } = require('discord.js'); 2 | 3 | const dolarService = require('../../services/dolarService'); 4 | const chileanPesoService = require('../../services/chileanPesoService'); 5 | const { formatNumber } = require('../../utils/numbers'); 6 | 7 | /** 8 | * Lista de alias válidos para el comando 9 | * 10 | * @return { Array } 11 | */ 12 | const aliases = () => ['cambio', 'cn', 'cmb']; 13 | 14 | /** 15 | * Información sobre el comando 16 | * 17 | * @return { Object } 18 | */ 19 | const help = () => ({ 20 | usage: '!cambio {B o D o P} {monto}', 21 | desc: 'Convierte USD o CLP o VED a USD/CLP/VED (sin incluir moneda origen) usando distintas tasas de cambio.', 22 | example: 23 | 'Si quiero convertir 10 Dolares Americanos a VED y CLP:\n!cambio d 10\n\nSi quiero convertir 500 Bolívares a USD y CLP:\n!cambio b 500\n\nSi quiero convertir 800 Pesos Chilenos a USD y VED:\n!cambio p 800' 24 | }); 25 | 26 | /** 27 | * Manejador del comando 28 | * 29 | * @param { Message } message Evento completo del mensaje 30 | */ 31 | const main = async (message) => { 32 | const prefix = process.env.PREFIX; 33 | const args = message.content.substring(prefix.length).split(' '); 34 | const mode = args[1] && typeof args[1] === 'string' ? args[1].toUpperCase() : null; 35 | const amount = parseFloat(args[2]); 36 | let title = null; 37 | const exchange = []; 38 | 39 | if (mode === null || !(mode === 'B' || mode === 'D' || mode === 'P') || isNaN(amount)) { 40 | return message.channel.send( 41 | 'Mano, no puedo hacer la conversión si faltan datos o los datos que me das están malos.' 42 | ); 43 | } 44 | 45 | const exchangeRatesB = await dolarService.getExchangeRates(true).catch(() => { 46 | message.channel.send('MonitorDolar dejó de funcionar... O me bloquearon, no sé.'); 47 | }); 48 | 49 | const exchangeRatesP = await chileanPesoService.getExchangeRates().catch(() => { 50 | message.channel.send('API tasas de cambio CLP dejó de funcionar... O me bloquearon, no sé.'); 51 | }); 52 | 53 | const exchangeRates = exchangeRatesB.concat(exchangeRatesP); 54 | 55 | switch (mode) { 56 | case 'B': 57 | title = `Cambio de VED a USD y CLP`; 58 | 59 | exchangeRates.forEach(exchangeRate => { 60 | let result = 0, resultFrom = '', resultTo = ''; 61 | 62 | switch (exchangeRate.nationalCurrency) { 63 | case 'CLP': 64 | resultFrom = 'VED'; 65 | resultTo = 'CLP'; 66 | // 1ro hay que pasar los bolos a USD, cristo perdoname por quemar valores 67 | result = (amount / exchangeRatesB[0].value) * exchangeRate.value; 68 | break; 69 | 70 | case 'VED': 71 | resultFrom = 'VED'; 72 | resultTo = 'USD'; 73 | result = amount / exchangeRate.value; 74 | break; 75 | } 76 | 77 | exchange.push({ 78 | name: `Tasa ${exchangeRate.title} (${exchangeRate.rate} ${exchangeRate.nationalCurrency}):`, 79 | value: `**${resultFrom} ${formatNumber(amount)}** => **${resultTo} ${formatNumber(result)}**` 80 | }); 81 | }); 82 | break; 83 | 84 | case 'D': 85 | title = `Cambio de USD a VED y CLP`; 86 | 87 | exchangeRates.forEach(exchangeRate => { 88 | let result = 0, resultFrom = '', resultTo = ''; 89 | 90 | switch (exchangeRate.nationalCurrency) { 91 | case 'CLP': 92 | resultFrom = 'USD'; 93 | resultTo = 'CLP'; 94 | result = amount * exchangeRate.value; 95 | break; 96 | 97 | case 'VED': 98 | resultFrom = 'USD'; 99 | resultTo = 'VED'; 100 | result = amount * exchangeRate.value; 101 | break; 102 | } 103 | 104 | exchange.push({ 105 | name: `Tasa ${exchangeRate.title} (${exchangeRate.rate} ${exchangeRate.nationalCurrency}):`, 106 | value: `**${resultFrom} ${formatNumber(amount)}** => **${resultTo} ${formatNumber(result)}**` 107 | }); 108 | }); 109 | break; 110 | 111 | case 'P': 112 | title = `Cambio de CLP a USD y VED`; 113 | 114 | exchangeRates.forEach(exchangeRate => { 115 | let result = 0, resultFrom = '', resultTo = ''; 116 | 117 | switch (exchangeRate.nationalCurrency) { 118 | case 'CLP': 119 | resultFrom = 'CLP'; 120 | resultTo = 'USD'; 121 | result = amount / exchangeRate.value; 122 | break; 123 | 124 | case 'VED': 125 | resultFrom = 'CLP'; 126 | resultTo = 'VED'; 127 | // 1ro hay que pasar los pesos a USD luego multiplicarlo por la tasa de USD->bolos, cristo perdoname por quemar valores 128 | result = amount / exchangeRatesP[0].value; 129 | result = result * exchangeRate.value; 130 | break; 131 | } 132 | 133 | exchange.push({ 134 | name: `Tasa ${exchangeRate.title} (${exchangeRate.rate} ${exchangeRate.nationalCurrency}):`, 135 | value: `**${resultFrom} ${formatNumber(amount)}** => **${resultTo} ${formatNumber(result)}**` 136 | }); 137 | }); 138 | break; 139 | } 140 | 141 | if (exchange.length === 0) { 142 | return message.channel.send( 143 | 'Mano no me cargo NI UNA tasa de cambio, que ladilla NOJODA.' 144 | ); 145 | } 146 | 147 | message.channel.send('Tuve que usar una calculadora, porque esto es demasiada matemática para mí', { 148 | embed: { 149 | color: 3141900, 150 | title, 151 | fields: exchange 152 | } 153 | }); 154 | }; 155 | 156 | module.exports = { aliases, help, main }; 157 | -------------------------------------------------------------------------------- /src/modules/cmd/ripper.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const cheerio = require('cheerio'); 3 | const fs = require('fs'); 4 | const zipFolder = require('zip-a-folder'); 5 | const rimraf = require('rimraf'); 6 | const { exec } = require('child_process'); 7 | const { Message } = require('discord.js'); 8 | const http = require('http'); 9 | const botUtils = require('../../utils/bot'); 10 | 11 | /** 12 | * Lista de alias válidos para el comando 13 | * 14 | * @return { Array } 15 | */ 16 | const aliases = () => ['rip', 'ripper', 'rippea']; 17 | 18 | /** 19 | * Información sobre el comando 20 | * 21 | * @return { Object } 22 | */ 23 | const help = () => ({ 24 | usage: '!rip', 25 | desc: 'Rippea un hilo de hispachan', 26 | example: '!rip https://www.hispachan.org/##/res/######.html#######' 27 | }); 28 | 29 | /** 30 | * Manejador del comando 31 | * 32 | * @param { Message } message Evento completo del mensaje 33 | */ 34 | const main = async (message) => { 35 | const url = botUtils.getParams(message.content); 36 | var info; 37 | 38 | function recursiveDownload(urlArray, nameArray, i, path) { 39 | if (i < urlArray.length) { 40 | if (!fs.existsSync(path)) { 41 | fs.mkdirSync(path); 42 | } 43 | 44 | if (!fs.existsSync(`${path}/${nameArray[i]}`)) { 45 | console.log(`[${i + 1}/${urlArray.length}] - ${nameArray[i]} - descargando`); 46 | 47 | request 48 | .get(urlArray[i]) 49 | .on('error', function (err) { 50 | console.log(err); 51 | }) 52 | .pipe(fs.createWriteStream(`${path}/${nameArray[i]}`)) 53 | .on('close', function () { 54 | recursiveDownload(urlArray, nameArray, i + 1, path); 55 | }); 56 | } else { 57 | console.log(`[${i + 1}/${urlArray.length}] - ${nameArray[i]} - ya existe`); 58 | recursiveDownload(urlArray, nameArray, i + 1); 59 | } 60 | } else { 61 | zipFolder.zipFolder(`./${path}`, `./${path}.zip`, function (err) { 62 | if (err) { 63 | console.error('Error al comprimir la carpeta rippeada: ' + err); 64 | } else { 65 | console.log('Subiendo archivo a File.io'); 66 | message.channel.send('Subiendo a File.io'); 67 | 68 | exec(`curl -F "file=@${path}.zip" https://file.io`, (err, stdout, stderr) => { 69 | if (err) { 70 | console.error('Error subiendo el archivo a file.io' + err); 71 | } else { 72 | const link = JSON.parse(stdout); 73 | console.log(`file.io upload result: ${link.success}`); 74 | 75 | if (link.success == false) { 76 | rimraf.sync(path); 77 | fs.unlinkSync(path + '.zip'); 78 | 79 | return message.channel.send(`Hubo un malpario error subiendo tu mierda a file.io`); 80 | } 81 | 82 | html = ` 83 | 84 | 85 | 107 | 108 | 109 | 110 |

Descargando desde ${info.host}/${info.tablon}/${info.hilo}

111 | Descargar zip
112 | `; 113 | 114 | src.forEach((el) => { 115 | html += ` 116 | 121 | `; 122 | }); 123 | 124 | html += ` 125 | 126 | 127 | `; 128 | 129 | // se crea el servidor http 130 | const requestListener = function (req, res) { 131 | res.writeHead(200, { 'Content-Type': 'text/html' }); 132 | res.end(html); 133 | server.close(); 134 | }; 135 | 136 | const server = http.createServer(requestListener); 137 | 138 | const host = process.env.HOST_ADDR || 'localhost'; 139 | const port = process.env.HOST_PORT || 8000; 140 | 141 | server.listen(port, host, () => { 142 | message.channel.send(`Descarga tu mierda en ${host}:${port}`); 143 | }); 144 | 145 | const sockets = new Set(); 146 | 147 | server.on('connection', (socket) => { 148 | sockets.add(socket); 149 | 150 | server.once('close', () => { 151 | sockets.delete(socket); 152 | }); 153 | }); 154 | 155 | /** 156 | * Muerte al http server. 157 | */ 158 | const close = (callback) => { 159 | for (const socket of sockets) { 160 | socket.destroy(); 161 | 162 | sockets.delete(socket); 163 | } 164 | 165 | server.close(callback); 166 | }; 167 | 168 | //message.channel.send(`${link.link}`); 169 | 170 | rimraf.sync(path); 171 | fs.unlinkSync(path + '.zip'); 172 | 173 | console.log('Rippeo terminado'); 174 | } 175 | }); 176 | } 177 | }); 178 | } 179 | } 180 | 181 | if (/(http[s]?:\/\/)?([^\/\s]+\/)(.*)/.test(url) == false) { 182 | console.error('Error: no hay url valida, url debe ser: https://www.hispachan.org/XX/res/XXX.html#XXX'); 183 | 184 | message.channel.send('URL invalida'); 185 | } else { 186 | const partes = url.split('/'); 187 | 188 | info = { 189 | host: partes[2], 190 | tablon: partes[3], 191 | hilo: partes[5].split('.')[0] 192 | }; 193 | 194 | const path = `${info.host}.${info.tablon}.${info.hilo}`; 195 | 196 | message.channel.send(`Rippeando ${info.host}/${info.tablon}/${info.hilo}`); 197 | 198 | if (!info.tablon || !info.hilo) { 199 | console.error('Error al obtener tablon o hilo'); 200 | 201 | message.channel.send(`Error al obtener tablon o hilo`); 202 | } else { 203 | var src = []; 204 | var names = []; 205 | 206 | console.log('Obteniendo HTML'); 207 | 208 | request(url, (error, resp, body) => { 209 | if (!error && resp.statusCode === 200) { 210 | console.log('Obteniendo href'); 211 | 212 | let $ = cheerio.load(body); 213 | 214 | // Obtiene el archivo del post que empezó el hilo 215 | $('.thread > span > a').each((i, el) => { 216 | let href = el.attribs.href; 217 | names.push(href.substring(href.lastIndexOf('/') + 1)); 218 | src.push(href); 219 | }); 220 | 221 | // Obtiene el resto de archivos 222 | $('.filenamereply > a').each((i, el) => { 223 | let href = el.attribs.href; 224 | names.push(href.substring(href.lastIndexOf('/') + 1)); 225 | src.push(href); 226 | }); 227 | 228 | if (src.length == 0) { 229 | console.error('No hay imagenes/videos para descargar'); 230 | message.channel.send(`No hay imagenes/videos para descargar`); 231 | } else { 232 | recursiveDownload(src, names, 0, path); 233 | } 234 | } 235 | }); 236 | } 237 | } 238 | }; 239 | 240 | module.exports = { aliases, help, main }; 241 | --------------------------------------------------------------------------------