From 576092fc9668d464511aea0e26d488f18deabb0a Mon Sep 17 00:00:00 2001 From: Leifer Mendez Date: Tue, 8 Nov 2022 21:41:16 +0100 Subject: [PATCH] . --- .gitignore | 4 +- __tests__/basic.e2e.test.js | 79 +++++ __tests__/basic.test.js | 163 ---------- lib/cli/bundle.cjs | 328 -------------------- lib/io/bundle.cjs | 141 --------- package-lock.json | 6 + package.json | 19 +- packages/README.md | 45 +-- packages/cli/lib/bundle.cjs | 236 +++++++------- packages/cli/package.json | 9 - packages/cli/rollup-cli.config.js | 2 +- packages/core/classes/bot.class.js | 32 ++ packages/core/index.js | 2 + packages/core/package.json | 21 ++ packages/core/rollup-cli.config.js | 10 + packages/core/tests/bot.class.test.js | 32 ++ packages/index.js | 5 + packages/io/classes/flow.class.js | 20 ++ packages/io/index.js | 5 +- packages/io/package.json | 10 - packages/io/rollup-cli.config.js | 2 +- packages/provider/TODO.md | 7 +- packages/provider/classes/provider.class.js | 9 + packages/provider/index.js | 2 + packages/provider/rollup-cli.config.js | 10 + packages/rollup-cli.config.js | 10 + 26 files changed, 408 insertions(+), 801 deletions(-) create mode 100644 __tests__/basic.e2e.test.js delete mode 100644 __tests__/basic.test.js delete mode 100644 lib/cli/bundle.cjs delete mode 100644 lib/io/bundle.cjs create mode 100644 packages/core/classes/bot.class.js create mode 100644 packages/core/index.js create mode 100644 packages/core/package.json create mode 100644 packages/core/rollup-cli.config.js create mode 100644 packages/core/tests/bot.class.test.js create mode 100644 packages/index.js create mode 100644 packages/io/classes/flow.class.js create mode 100644 packages/provider/classes/provider.class.js create mode 100644 packages/provider/index.js create mode 100644 packages/provider/rollup-cli.config.js create mode 100644 packages/rollup-cli.config.js diff --git a/.gitignore b/.gitignore index 9c86698..f9ca563 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,6 @@ mediaSend/* packages/cli/config.json config.json coverage/ -*.lcov \ No newline at end of file +*.lcov +lib +tmp/ \ No newline at end of file diff --git a/__tests__/basic.e2e.test.js b/__tests__/basic.e2e.test.js new file mode 100644 index 0000000..b95cfac --- /dev/null +++ b/__tests__/basic.e2e.test.js @@ -0,0 +1,79 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +/** + * require('@bot-whatsapp') + */ +const { inout, provider, bot } = require('../lib/index.cjs') + +/** + * MockDB + */ +class DatabaseClass { + constructor() {} + + saveLog = (ctx) => { + return ctx + } +} + +const adapterDB = new DatabaseClass() + +const adapterFlow = new inout.instance( + (() => { + const flowA = inout + .addKeyword('hola') + .addAnswer('Bienvenido a tu tienda 🥲') + .addAnswer('escribe *catalogo* o *ofertas*') + .toJson() + + const flowB = inout + .addKeyword(['catalogo', 'ofertas']) + .addAnswer('Este es nuestro CATALOGO mas reciente!', { + buttons: [{ body: 'Xiaomi' }, { body: 'Samsung' }], + }) + .toJson() + + const flowC = inout + .addKeyword('Xiaomi') + .addAnswer('Estos son nuestro productos XIAOMI ....', { + media: 'https://....', + }) + .addAnswer('Si quieres mas info escrbie *info*') + .toJson() + + const flowD = inout + .addKeyword('chao!') + .addAnswer('bye!') + .addAnswer('Recuerda que tengo esta promo', { + media: 'https://media2.giphy.com/media/VQJu0IeULuAmCwf5SL/giphy.gif', + }) + .toJson() + + const flowE = inout + .addKeyword('Modelo C', { sensitive: false }) + .addAnswer('100USD', { media: 'http//:...' }) + .toJson() + + return [...flowA, ...flowB, ...flowC, ...flowC, ...flowD, ...flowE] + })() +) + +const adapterProvider = new provider.instance() + +test(`[Flow Basico]: BotClass`, () => { + let messages = [] + + const flows = adapterFlow + const databases = adapterDB + const providers = adapterProvider + + const botBasic = new bot.instance(flows, databases, providers) + + botBasic.on('message', (ctx) => messages.push(ctx.body)) + botBasic.emit('message', { body: 'hola' }) + botBasic.emit('message', { body: 'otro' }) + + assert.is(messages.join(','), ['hola', 'otro'].join(',')) +}) + +test.run() diff --git a/__tests__/basic.test.js b/__tests__/basic.test.js deleted file mode 100644 index 16e3ac0..0000000 --- a/__tests__/basic.test.js +++ /dev/null @@ -1,163 +0,0 @@ -const { test } = require('uvu') -const assert = require('uvu/assert') -const { EventEmitter } = require('node:events') -const { addKeyword } = require('../packages/io') -const database = require('mime-db') - -const flow = addKeyword('hola') - .addAnswer('bienvenido') - .addAnswer('chao') - .toJson() - -const provider = { - sendMessage: (ctx) => { - console.log('Enviando...', ctx) - }, -} -//// DataBaseMock --------------------------------------------- -class DataBaseMock { - flow - provider - constructor(_flow, _provider) { - this.flow = _flow - this.provider = _provider - } - - continue = (message, ref = false) => { - let keyRef = ref - let ansRef = null - if (!keyRef) { - keyRef = this.flow.find((n) => n.keyword.includes(message)).ref - } - ansRef = this.flow.find((n) => n.keyword === keyRef) - - if (ansRef) { - this.provider.sendMessage(ansRef.answer) - this.continue(null, ansRef.ref) - } - } -} -//// ProviderMock --------------------------------------------- -class ProviderMock { - constructor() { - //twilio ... - } - - sendMessage = (ctx) => { - console.log('Enviando...', ctx) - } -} - -// const bot = { -// start: ({ flow, database, provider }) => { -// // console.log(database instanceof DataBaseMock) -// const flowCtx = database -// const botEmitter = new MyEmitter() - -// botEmitter.on('message', (ctx) => flowCtx.continue(ctx.body)) -// return botEmitter -// }, -// } - -//// BotMock --------------------------------------------- - -// test(`[Flow Basico]: Saludar y Responder`, () => { -// let messages = [] - -// const botBasic = new BotMock( -// flow, -// new DataBaseMock(flow, provider), -// provider -// ) - -// botBasic.on('message', (ctx) => messages.push(ctx.body)) - -// // Esta linea emula el llegar un mensaje! -// botBasic.emit('message', { body: 'hola' }) - -// assert.is(messages.join(','), 'hola') -// }) - -// test.run() - -/*** - * NEW - */ - -class BotClass extends EventEmitter { - flowClass - databaseClass - providerClass - constructor(_flow, _database, _provider) { - super() - this.flowClass = _flow - this.databaseClass = _database - this.providerClass = _provider - - this.on('message', (ctxMessage) => { - this.databaseClass.saveLog(ctxMessage) - this.continue(ctxMessage.body) - }) - } - - continue = (message, ref = false) => { - const responde = this.flowClass.find(message, ref) - if (responde) { - this.providerClass.sendMessage(responde.answer) - this.continue(null, responde.ref) - } - } -} - -class ProviderClass { - constructor() {} - - sendMessage = (message) => { - console.log('Enviar...', message) - } -} - -class FlowClass { - flow - constructor(_flow) { - this.flow = _flow - } - - find = (message, ref = false) => { - let keyRef = ref - let ansRef = null - if (!keyRef) { - keyRef = - this.flow.find((n) => n.keyword.includes(message))?.ref || null - } - ansRef = this.flow.find((n) => n.keyword === keyRef) - if (ansRef) return ansRef - return false - } -} - -class DatabaseClass { - constructor() {} - - saveLog = (ctx) => { - console.log('Guardando...', ctx) - } -} - -test(`[Flow Basico]: Saludar y Responder`, () => { - let messages = [] - - const botBasic = new BotClass( - new FlowClass(flow), - new DatabaseClass(), - new ProviderClass() - ) - - botBasic.on('message', (ctx) => messages.push(ctx.body)) - - botBasic.emit('message', { body: 'hola' }) - - assert.is(messages.join(','), 'hola') -}) - -test.run() diff --git a/lib/cli/bundle.cjs b/lib/cli/bundle.cjs deleted file mode 100644 index b6bae37..0000000 --- a/lib/cli/bundle.cjs +++ /dev/null @@ -1,328 +0,0 @@ -'use strict'; - -var require$$0$3 = require('prompts'); -var require$$0 = require('kleur'); -var require$$0$1 = require('fs'); -var require$$1$1 = require('path'); -var require$$1 = require('cross-spawn'); -var require$$0$2 = require('rimraf'); - -const { red: red$2 } = require$$0; -const spawn = require$$1; -// const { detect } = require('detect-package-manager') -const PKG_OPTION = { - npm: 'install', - yarn: 'add', - pnpm: 'add', -}; - -const getPkgManage$1 = async () => { - // const pkg = await detect() - // return pkg - return 'npm' -}; - -const installDeps$1 = (pkgManager, packageList) => { - const errorMessage = `Ocurrio un error instalando ${packageList}`; - let childProcess = []; - - const installSingle = (pkgInstall) => () => { - new Promise((resolve) => { - try { - childProcess = spawn( - pkgManager, - [PKG_OPTION[pkgManager], pkgInstall], - { - stdio: 'inherit', - } - ); - - childProcess.on('error', (e) => { - console.error(e); - console.error(red$2(errorMessage)); - resolve(); - }); - - childProcess.on('close', (code) => { - if (code === 0) { - resolve(); - } else { - console.error(code); - console.error(red$2(errorMessage)); - } - }); - - resolve(); - } catch (e) { - console.error(e); - console.error(red$2(errorMessage)); - } - }); - }; - - if (typeof packageList === 'string') { - childProcess.push(installSingle(packageList)); - } else { - for (const pkg of packageList) { - childProcess.push(installSingle(pkg)); - } - } - - const runInstall = () => { - return Promise.all(childProcess.map((i) => i())) - }; - return { runInstall } -}; - -var tool = { getPkgManage: getPkgManage$1, installDeps: installDeps$1 }; - -const { readFileSync, existsSync } = require$$0$1; -const { join: join$2 } = require$$1$1; -const { installDeps, getPkgManage } = tool; - -const PATHS_DIR = [ - join$2(__dirname, 'pkg-to-update.json'), - join$2(__dirname, '..', 'pkg-to-update.json'), -]; - -const PKG_TO_UPDATE = () => { - const PATH_INDEX = PATHS_DIR.findIndex((a) => existsSync(a)); - const data = readFileSync(PATHS_DIR[PATH_INDEX], 'utf-8'); - const dataParse = JSON.parse(data); - const pkg = Object.keys(dataParse).map((n) => `${n}@${dataParse[n]}`); - return pkg -}; - -const installAll$1 = async () => { - const pkg = await getPkgManage(); - installDeps(pkg, PKG_TO_UPDATE()).runInstall(); -}; - -var install = { installAll: installAll$1 }; - -const rimraf = require$$0$2; -const { yellow: yellow$2 } = require$$0; -const { join: join$1 } = require$$1$1; - -const PATH_WW = [ - join$1(process.cwd(), '.wwebjs_auth'), - join$1(process.cwd(), 'session.json'), -]; - -const cleanSession$1 = () => { - const queue = []; - for (const PATH of PATH_WW) { - console.log(yellow$2(`😬 Eliminando: ${PATH}`)); - queue.push(rimraf(PATH, () => Promise.resolve())); - } - return Promise.all(queue) -}; - -var clean = { cleanSession: cleanSession$1 }; - -const { red: red$1, yellow: yellow$1, green, bgCyan } = require$$0; - -const checkNodeVersion$1 = () => { - console.log(bgCyan('🚀 Revisando tu Node.js')); - const version = process.version; - const majorVersion = parseInt(version.replace('v', '').split('.').shift()); - if (majorVersion < 16) { - console.error( - red$1( - `🔴 Se require Node.js 16 o superior. Actualmente esta ejecutando Node.js ${version}` - ) - ); - process.exit(1); - } - console.log(green(`Node.js combatible ${version}`)); - console.log(``); -}; - -const checkOs$1 = () => { - console.log(bgCyan('🙂 Revisando tu Sistema Operativo')); - const os = process.platform; - if (!os.includes('win32')) { - const messages = [ - `El sistema operativo actual (${os}) posiblemente requiera`, - `una confiuración adicional referente al puppeter`, - ``, - `Recuerda pasar por el WIKI`, - `🔗 https://github.com/leifermendez/bot-whatsapp/wiki/Instalaci%C3%B3n`, - ``, - ]; - - console.log(yellow$1(messages.join(' \n'))); - } - - console.log(``); -}; - -var check = { checkNodeVersion: checkNodeVersion$1, checkOs: checkOs$1 }; - -const { writeFile } = require$$0$1.promises; -const { join } = require$$1$1; - -/** - * JSON_TEMPLATE = {[key:string]{...pros}} - */ -const JSON_TEMPLATE = { - provider: { - vendor: '', - }, - database: { - host: '', - password: '', - port: '', - username: '', - db: '', - }, - io: { - vendor: '', - }, -}; - -const PATH_CONFIG = join(process.cwd(), 'config.json'); - -const jsonConfig$1 = () => { - return writeFile( - PATH_CONFIG, - JSON.stringify(JSON_TEMPLATE, null, 2), - 'utf-8' - ) -}; - -var configuration = { jsonConfig: jsonConfig$1 }; - -const prompts = require$$0$3; -const { yellow, red } = require$$0; -const { installAll } = install; -const { cleanSession } = clean; -const { checkNodeVersion, checkOs } = check; -const { jsonConfig } = configuration; - -const startInteractive$1 = async () => { - const questions = [ - { - type: 'text', - name: 'dependencies', - message: - 'Quieres actualizar las librerias "whatsapp-web.js"? (Y/n)', - }, - { - type: 'text', - name: 'cleanTmp', - message: 'Quieres limpiar la session del bot? (Y/n)', - }, - { - type: 'multiselect', - name: 'providerWs', - message: 'Proveedor de Whatsapp', - choices: [ - { title: 'whatsapp-web.js', value: 'whatsapp-web.js' }, - { title: 'API Oficial (Meta)', value: 'meta', disabled: true }, - { title: 'Twilio', value: 'twilio', disabled: true }, - ], - max: 1, - hint: 'Espacio para selecionar', - instructions: '↑/↓', - }, - { - type: 'multiselect', - name: 'providerDb', - message: 'Cual base de datos quieres usar', - choices: [ - { title: 'JSONFile', value: 'json' }, - { title: 'MySQL', value: 'mysql', disabled: true }, - { title: 'Mongo', value: 'mongo', disabled: true }, - ], - max: 1, - hint: 'Espacio para selecionar', - instructions: '↑/↓', - }, - ]; - - console.clear(); - checkNodeVersion(); - checkOs(); - const onCancel = () => { - console.log('Proceso cancelado!'); - return true - }; - const response = await prompts(questions, { onCancel }); - const { - dependencies = '', - cleanTmp = '', - providerDb = [], - providerWs = [], - } = response; - /** - * Question #1 - * @returns - */ - const installOrUdpateDep = async () => { - const answer = dependencies.toLowerCase() || 'n'; - if (answer.includes('n')) return true - - if (answer.includes('y')) { - await installAll(); - return true - } - }; - - /** - * Question #2 - * @returns - */ - const cleanAllSession = async () => { - const answer = cleanTmp.toLowerCase() || 'n'; - if (answer.includes('n')) return true - - if (answer.includes('y')) { - await cleanSession(); - return true - } - }; - - const vendorProvider = async () => { - if (!providerWs.length) { - console.log( - red( - `Debes de seleccionar una WS Provider. Tecla [Space] para seleccionar` - ) - ); - process.exit(1); - } - console.log(yellow(`'Deberia crer una carpeta en root/provider'`)); - return true - }; - - const dbProvider = async () => { - const answer = providerDb; - if (!providerDb.length) { - console.log( - red( - `Debes de seleccionar una DB Provider. Tecla [Space] para seleccionar` - ) - ); - process.exit(1); - } - if (answer === 'json') { - console.log('Deberia crer una carpeta en root/data'); - return 1 - } - }; - - await installOrUdpateDep(); - await cleanAllSession(); - await vendorProvider(); - await dbProvider(); - await jsonConfig(); -}; - -var interactive = { startInteractive: startInteractive$1 }; - -const { startInteractive } = interactive; -if (process.env.NODE_ENV === 'dev') startInteractive(); -var cli = { startInteractive }; - -module.exports = cli; diff --git a/lib/io/bundle.cjs b/lib/io/bundle.cjs deleted file mode 100644 index 3e97d32..0000000 --- a/lib/io/bundle.cjs +++ /dev/null @@ -1,141 +0,0 @@ -'use strict'; - -var require$$0 = require('crypto'); - -const crypto = require$$0; - -const generateRef$3 = () => { - return crypto.randomUUID() -}; - -var hash = { generateRef: generateRef$3 }; - -const { generateRef: generateRef$2 } = hash; - -var utils = { generateRef: generateRef$2 }; - -const toJson$3 = (inCtx) => () => { - const lastCtx = inCtx.hasOwnProperty('ctx') ? inCtx.ctx : inCtx; - return lastCtx.json -}; - -var toJson_1 = { toJson: toJson$3 }; - -const { generateRef: generateRef$1 } = utils; -const { toJson: toJson$2 } = toJson_1; -/** - * - * @param answer string - * @param options {media:string, buttons:[], capture:true default false} - * @returns - */ -const addAnswer$3 = (inCtx) => (answer, options) => { - const getAnswerOptions = () => ({ - media: typeof options?.media === 'string' ? `${options?.media}` : null, - buttons: Array.isArray(options?.buttons) ? options.buttons : [], - capture: - typeof options?.capture === 'boolean' ? options?.capture : false, - }); - - const lastCtx = inCtx.hasOwnProperty('ctx') ? inCtx.ctx : inCtx; - const ctxAnswer = () => { - const ref = `ans_${generateRef$1()}`; - - const options = { - ...getAnswerOptions(), - keyword: {}, - }; - - const json = [].concat(inCtx.json).concat([ - { - ref, - keyword: lastCtx.ref, - answer, - options, - }, - ]); - - return { ...lastCtx, ref, answer, json, options } - }; - - const ctx = ctxAnswer(); - - return { - ctx, - ref: ctx.ref, - addAnswer: addAnswer$3(ctx), - toJson: toJson$2(ctx), - } -}; - -var addAnswer_1 = { addAnswer: addAnswer$3 }; - -const { generateRef } = utils; -const { addAnswer: addAnswer$2 } = addAnswer_1; -const { toJson: toJson$1 } = toJson_1; -/** - * addKeyword: - * Es necesario que genere id|hash - */ - -/** - * - * @param {*} message `string | string[]` - * @param {*} options {sensitive:boolean} default false - */ -const addKeyword$2 = (keyword, options) => { - /** - * Esta funcion deberia parsear y validar las opciones - * del keyword - * @returns - */ - const parseOptions = () => { - const defaultProperties = { - sensitive: - typeof options?.sensitive === 'boolean' - ? options?.sensitive - : false, - }; - - return defaultProperties - }; - - const ctxAddKeyword = () => { - const ref = `key_${generateRef()}`; - const options = parseOptions(); - const json = [ - { - ref, - keyword, - options, - }, - ]; - /** - * Se guarda en db - */ - - return { ref, keyword, options, json } - }; - - const ctx = ctxAddKeyword(); - - return { - ctx, - ref: ctx.ref, - addAnswer: addAnswer$2(ctx), - toJson: toJson$1(ctx), - } -}; - -var addKeyword_1 = { addKeyword: addKeyword$2 }; - -const { addAnswer: addAnswer$1 } = addAnswer_1; -const { addKeyword: addKeyword$1 } = addKeyword_1; -const { toJson } = toJson_1; - -var methods = { addAnswer: addAnswer$1, addKeyword: addKeyword$1, toJson }; - -const { addKeyword, addAnswer } = methods; -var io = { addKeyword, addAnswer }; - -module.exports = io; diff --git a/package-lock.json b/package-lock.json index 45758ef..e1e8de9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2527,6 +2527,12 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, + "husky": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.2.tgz", + "integrity": "sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", diff --git a/package.json b/package.json index 2d2ad2c..ec65ab7 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,26 @@ "description": "Bot de wahtsapp open source para MVP o pequeños negocios", "main": "app.js", "scripts": { - "cli": "node ./packages/cli/bin/cli.js", "cli:rollup": "rollup ./packages/cli/index.js --config ./packages/cli/rollup-cli.config.js", "io:rollup": "rollup ./packages/io/index.js --config ./packages/io/rollup-cli.config.js", + "core:rollup": "rollup ./packages/core/index.js --config ./packages/core/rollup-cli.config.js", + "provider:rollup": "rollup ./packages/provider/index.js --config ./packages/provider/rollup-cli.config.js", + "all:rollup": "rollup ./packages/index.js --config ./packages/rollup-cli.config.js", + "format:check": "prettier --check ./packages", + "format:write": "prettier --write ./packages", + "lint:check": "eslint ./packages", + "lint:fix": "eslint --fix ./packages", + "build": "npm run all:rollup", "test.unit": "node ./node_modules/uvu/bin.js packages test", "test.e2e": "node ./node_modules/uvu/bin.js __tests__ test", - "test.coverage": "node ./node_modules/c8/bin/c8.js --check-coverage --lines=90 npm run test.unit" + "test.coverage": "node ./node_modules/c8/bin/c8.js --check-coverage --lines=90 npm run test.unit", + "cli": "node ./packages/cli/bin/cli.js" + }, + "husky": { + "hooks": { + "pre-commit": "npm run format:check && npm run lint:fix", + "pre-push": "npm run test" + } }, "workspaces": [ "packages/cli", @@ -63,6 +77,7 @@ "detect-package-manager": "^2.0.1", "eslint": "^8.26.0", "eslint-config-prettier": "^8.5.0", + "husky": "^8.0.2", "kleur": "^4.1.5", "pm2": "^5.2.0", "prettier": "^2.7.1", diff --git a/packages/README.md b/packages/README.md index 6214268..533ac37 100644 --- a/packages/README.md +++ b/packages/README.md @@ -3,37 +3,42 @@ Se separaran responsabilidades del proyecto en diferentes packages, de esta manera se podra versionar y controlar los diferentes versionamientos y cambios con un mayor desacoplamiento. ##### Principales Funciones - El bot tiene tres funciones principales hasta este momento la cuales divideremos en paquetes para que puedan trabajarse y por separador sin acoplamiento. -__Package CLI__ (*Command Line Interface*) +El bot tiene tres funciones principales hasta este momento la cuales divideremos en paquetes para que puedan trabajarse y por separador sin acoplamiento. + +**Package CLI** (_Command Line Interface_) + > Sera basicamente un asistente via `cosola` el cual nos ayudara a realizar las instalacion de las dependencias necesarias y a crear un archivo de configuracion para tener un migrado rápido. -> La idea esque se pueda ejecutar un commando parecido a `npm create bot@leifermendez` o algo parecido y comienze a instalar todo. +> La idea esque se pueda ejecutar un commando parecido a `npm create bot@leifermendez` o algo parecido y comienze a instalar todo. 🤞 Funciones deseadas: -- Que actualice y corrija los problema de versionamientos más frecuetes -- Verificar la versión de NODE correcta -- Verificar OS para brindar mejor soporte de puppeter -- Limpiar sesion, borrar carpeta de sesion -- Seleccionar provider -- Poder usar un archivo .json con la configuración +- Que actualice y corrija los problema de versionamientos más frecuetes +- Verificar la versión de NODE correcta +- Verificar OS para brindar mejor soporte de puppeter +- Limpiar sesion, borrar carpeta de sesion +- Seleccionar provider +- Poder usar un archivo .json con la configuración +**Package Provider** -__Package Provider__ > Es parte clave del proyecto la idea es poder tener la opcion de incluir otro proveedor de mensajeria como la api oficial o api de twilio -- WhatsappWeb (*default*) -- Whatsapp API official -- Twilio +- WhatsappWeb (_default_) +- Whatsapp API official +- Twilio + +**Package Input/Output** -__Package Input/Output__ > Gestionar los diferentes mensajes entranates y poder responder, a la vez de mantener un registro de los datos ---------- -__Comunidad__ +--- + +**Comunidad** + > Forma parte de este proyecto. -- [Discord](https://link.codigoencasa.com/DISCORD) -- [Twitter](https://twitter.com/leifermendez) -- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR) -- [Telegram](https://t.me/leifermendez) \ No newline at end of file +- [Discord](https://link.codigoencasa.com/DISCORD) +- [Twitter](https://twitter.com/leifermendez) +- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR) +- [Telegram](https://t.me/leifermendez) diff --git a/packages/cli/lib/bundle.cjs b/packages/cli/lib/bundle.cjs index 7d68589..95f7192 100644 --- a/packages/cli/lib/bundle.cjs +++ b/packages/cli/lib/bundle.cjs @@ -1,30 +1,30 @@ -'use strict'; +'use strict' -var require$$0$3 = require('prompts'); -var require$$0 = require('kleur'); -var require$$0$1 = require('fs'); -var require$$1$1 = require('path'); -var require$$1 = require('cross-spawn'); -var require$$2 = require('detect-package-manager'); -var require$$0$2 = require('rimraf'); +var require$$0$3 = require('prompts') +var require$$0 = require('kleur') +var require$$0$1 = require('fs') +var require$$1$1 = require('path') +var require$$1 = require('cross-spawn') +var require$$2 = require('detect-package-manager') +var require$$0$2 = require('rimraf') -const { red: red$2 } = require$$0; -const spawn = require$$1; -const { detect } = require$$2; +const { red: red$2 } = require$$0 +const spawn = require$$1 +const { detect } = require$$2 const PKG_OPTION = { npm: 'install', yarn: 'add', pnpm: 'add', -}; +} const getPkgManage = async () => { - const pkg = await detect(); + const pkg = await detect() return pkg -}; +} const installDeps$1 = (pkgManager, packageList) => { - const errorMessage = `Ocurrio un error instalando ${packageList}`; - let childProcess = []; + const errorMessage = `Ocurrio un error instalando ${packageList}` + let childProcess = [] const installSingle = (pkgInstall) => () => { new Promise((resolve) => { @@ -35,112 +35,112 @@ const installDeps$1 = (pkgManager, packageList) => { { stdio: 'inherit', } - ); + ) childProcess.on('error', (e) => { - console.error(e); - console.error(red$2(errorMessage)); - resolve(); - }); + console.error(e) + console.error(red$2(errorMessage)) + resolve() + }) childProcess.on('close', (code) => { if (code === 0) { - resolve(); + resolve() } else { - console.error(code); - console.error(red$2(errorMessage)); + console.error(code) + console.error(red$2(errorMessage)) } - }); + }) - resolve(); + resolve() } catch (e) { - console.error(e); - console.error(red$2(errorMessage)); + console.error(e) + console.error(red$2(errorMessage)) } - }); - }; + }) + } if (typeof packageList === 'string') { - childProcess.push(installSingle(packageList)); + childProcess.push(installSingle(packageList)) } else { for (const pkg of packageList) { - childProcess.push(installSingle(pkg)); + childProcess.push(installSingle(pkg)) } } const runInstall = () => { return Promise.all(childProcess.map((i) => i())) - }; + } return { runInstall } -}; +} -var tool = { getPkgManage, installDeps: installDeps$1 }; +var tool = { getPkgManage, installDeps: installDeps$1 } -const { readFileSync, existsSync } = require$$0$1; -const { join: join$2 } = require$$1$1; -const { installDeps } = tool; +const { readFileSync, existsSync } = require$$0$1 +const { join: join$2 } = require$$1$1 +const { installDeps } = tool const PATHS_DIR = [ join$2(__dirname, 'pkg-to-update.json'), join$2(__dirname, '..', 'pkg-to-update.json'), -]; +] const PKG_TO_UPDATE = () => { - const PATH_INDEX = PATHS_DIR.findIndex((a) => existsSync(a)); - const data = readFileSync(PATHS_DIR[PATH_INDEX], 'utf-8'); - const dataParse = JSON.parse(data); - const pkg = Object.keys(dataParse).map((n) => `${n}@${dataParse[n]}`); + const PATH_INDEX = PATHS_DIR.findIndex((a) => existsSync(a)) + const data = readFileSync(PATHS_DIR[PATH_INDEX], 'utf-8') + const dataParse = JSON.parse(data) + const pkg = Object.keys(dataParse).map((n) => `${n}@${dataParse[n]}`) return pkg -}; +} const installAll$1 = async () => { // const pkg = await getPkgManage() - installDeps('npm', PKG_TO_UPDATE()).runInstall(); -}; + installDeps('npm', PKG_TO_UPDATE()).runInstall() +} -var install = { installAll: installAll$1 }; +var install = { installAll: installAll$1 } -const rimraf = require$$0$2; -const { yellow: yellow$2 } = require$$0; -const { join: join$1 } = require$$1$1; +const rimraf = require$$0$2 +const { yellow: yellow$2 } = require$$0 +const { join: join$1 } = require$$1$1 const PATH_WW = [ join$1(process.cwd(), '.wwebjs_auth'), join$1(process.cwd(), 'session.json'), -]; +] const cleanSession$1 = () => { - const queue = []; + const queue = [] for (const PATH of PATH_WW) { - console.log(yellow$2(`😬 Eliminando: ${PATH}`)); - queue.push(rimraf(PATH, () => Promise.resolve())); + console.log(yellow$2(`😬 Eliminando: ${PATH}`)) + queue.push(rimraf(PATH, () => Promise.resolve())) } return Promise.all(queue) -}; +} -var clean = { cleanSession: cleanSession$1 }; +var clean = { cleanSession: cleanSession$1 } -const { red: red$1, yellow: yellow$1, green, bgCyan } = require$$0; +const { red: red$1, yellow: yellow$1, green, bgCyan } = require$$0 const checkNodeVersion$1 = () => { - console.log(bgCyan('🚀 Revisando tu Node.js')); - const version = process.version; - const majorVersion = parseInt(version.replace('v', '').split('.').shift()); + console.log(bgCyan('🚀 Revisando tu Node.js')) + const version = process.version + const majorVersion = parseInt(version.replace('v', '').split('.').shift()) if (majorVersion < 16) { console.error( red$1( `🔴 Se require Node.js 16 o superior. Actualmente esta ejecutando Node.js ${version}` ) - ); - process.exit(1); + ) + process.exit(1) } - console.log(green(`Node.js combatible ${version}`)); - console.log(``); -}; + console.log(green(`Node.js combatible ${version}`)) + console.log(``) +} const checkOs$1 = () => { - console.log(bgCyan('🙂 Revisando tu Sistema Operativo')); - const os = process.platform; + console.log(bgCyan('🙂 Revisando tu Sistema Operativo')) + const os = process.platform if (!os.includes('win32')) { const messages = [ `El sistema operativo actual (${os}) posiblemente requiera`, @@ -149,18 +149,18 @@ const checkOs$1 = () => { `Recuerda pasar por el WIKI`, `🔗 https://github.com/leifermendez/bot-whatsapp/wiki/Instalaci%C3%B3n`, ``, - ]; + ] - console.log(yellow$1(messages.join(' \n'))); + console.log(yellow$1(messages.join(' \n'))) } - console.log(``); -}; + console.log(``) +} -var check = { checkNodeVersion: checkNodeVersion$1, checkOs: checkOs$1 }; +var check = { checkNodeVersion: checkNodeVersion$1, checkOs: checkOs$1 } -const { writeFile } = require$$0$1.promises; -const { join } = require$$1$1; +const { writeFile } = require$$0$1.promises +const { join } = require$$1$1 /** * JSON_TEMPLATE = {[key:string]{...pros}} @@ -179,9 +179,9 @@ const JSON_TEMPLATE = { io: { vendor: '', }, -}; +} -const PATH_CONFIG = join(process.cwd(), 'config.json'); +const PATH_CONFIG = join(process.cwd(), 'config.json') const jsonConfig$1 = () => { return writeFile( @@ -189,16 +189,16 @@ const jsonConfig$1 = () => { JSON.stringify(JSON_TEMPLATE, null, 2), 'utf-8' ) -}; +} -var configuration = { jsonConfig: jsonConfig$1 }; +var configuration = { jsonConfig: jsonConfig$1 } -const prompts = require$$0$3; -const { yellow, red } = require$$0; -const { installAll } = install; -const { cleanSession } = clean; -const { checkNodeVersion, checkOs } = check; -const { jsonConfig } = configuration; +const prompts = require$$0$3 +const { yellow, red } = require$$0 +const { installAll } = install +const { cleanSession } = clean +const { checkNodeVersion, checkOs } = check +const { jsonConfig } = configuration const startInteractive$1 = async () => { const questions = [ @@ -239,49 +239,49 @@ const startInteractive$1 = async () => { hint: 'Espacio para selecionar', instructions: '↑/↓', }, - ]; + ] - console.clear(); - checkNodeVersion(); - checkOs(); + console.clear() + checkNodeVersion() + checkOs() const onCancel = () => { - console.log('Proceso cancelado!'); + console.log('Proceso cancelado!') return true - }; - const response = await prompts(questions, { onCancel }); + } + const response = await prompts(questions, { onCancel }) const { dependencies = '', cleanTmp = '', providerDb = [], providerWs = [], - } = response; + } = response /** * Question #1 * @returns */ const installOrUdpateDep = async () => { - const answer = dependencies.toLowerCase() || 'n'; + const answer = dependencies.toLowerCase() || 'n' if (answer.includes('n')) return true if (answer.includes('y')) { - await installAll(); + await installAll() return true } - }; + } /** * Question #2 * @returns */ const cleanAllSession = async () => { - const answer = cleanTmp.toLowerCase() || 'n'; + const answer = cleanTmp.toLowerCase() || 'n' if (answer.includes('n')) return true if (answer.includes('y')) { - await cleanSession(); + await cleanSession() return true } - }; + } const vendorProvider = async () => { if (!providerWs.length) { @@ -289,40 +289,40 @@ const startInteractive$1 = async () => { red( `Debes de seleccionar una WS Provider. Tecla [Space] para seleccionar` ) - ); - process.exit(1); + ) + process.exit(1) } - console.log(yellow(`'Deberia crer una carpeta en root/provider'`)); + console.log(yellow(`'Deberia crer una carpeta en root/provider'`)) return true - }; + } const dbProvider = async () => { - const answer = providerDb; + const answer = providerDb if (!providerDb.length) { console.log( red( `Debes de seleccionar una DB Provider. Tecla [Space] para seleccionar` ) - ); - process.exit(1); + ) + process.exit(1) } if (answer === 'json') { - console.log('Deberia crer una carpeta en root/data'); + console.log('Deberia crer una carpeta en root/data') return 1 } - }; + } - await installOrUdpateDep(); - await cleanAllSession(); - await vendorProvider(); - await dbProvider(); - await jsonConfig(); -}; + await installOrUdpateDep() + await cleanAllSession() + await vendorProvider() + await dbProvider() + await jsonConfig() +} -var interactive = { startInteractive: startInteractive$1 }; +var interactive = { startInteractive: startInteractive$1 } -const { startInteractive } = interactive; -if (process.env.NODE_ENV === 'dev') startInteractive(); -var cli = { startInteractive }; +const { startInteractive } = interactive +if (process.env.NODE_ENV === 'dev') startInteractive() +var cli = { startInteractive } -module.exports = cli; +module.exports = cli diff --git a/packages/cli/package.json b/packages/cli/package.json index 6c1cb19..6c0a809 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -4,15 +4,6 @@ "description": "", "main": "index.js", "private": true, - "scripts": { - "cli:dev": "cross-env NODE_ENV=dev node ./index.js", - "cli:rollup": "rollup index.js --config ./rollup-cli.config.js", - "format:check": "prettier --check .", - "format:write": "prettier --write .", - "lint:check": "eslint .", - "lint:fix": "eslint --fix .", - "build:cli": "npm run format:write && npm run lint:fix && npm run cli:rollup" - }, "keywords": [], "author": "", "license": "ISC" diff --git a/packages/cli/rollup-cli.config.js b/packages/cli/rollup-cli.config.js index e030bb2..02f9e6b 100644 --- a/packages/cli/rollup-cli.config.js +++ b/packages/cli/rollup-cli.config.js @@ -3,7 +3,7 @@ const commonjs = require('@rollup/plugin-commonjs') module.exports = { input: 'index.js', output: { - file: 'lib/cli/bundle.cjs', + file: 'lib/cli/bundle.cli.cjs', format: 'cjs', }, plugins: [commonjs()], diff --git a/packages/core/classes/bot.class.js b/packages/core/classes/bot.class.js new file mode 100644 index 0000000..cee96b1 --- /dev/null +++ b/packages/core/classes/bot.class.js @@ -0,0 +1,32 @@ +const { EventEmitter } = require('node:events') + +/** + * Clase principal del BOT + */ +class BotClass extends EventEmitter { + flowClass + databaseClass + providerClass + constructor(_flow, _database, _provider) { + super() + this.flowClass = _flow + this.databaseClass = _database + this.providerClass = _provider + + this.on('message', (ctxMessage) => this.handleOnMessage(ctxMessage)) + } + + handleOnMessage = (ctxMessage) => { + this.databaseClass.saveLog(ctxMessage) + this.continue(ctxMessage.body) + } + + continue = (message, ref = false) => { + const responde = this.flowClass.find(message, ref) + if (responde) { + this.providerClass.sendMessage(responde.answer) + this.continue(null, responde.ref) + } + } +} +module.exports = BotClass diff --git a/packages/core/index.js b/packages/core/index.js new file mode 100644 index 0000000..80a28cc --- /dev/null +++ b/packages/core/index.js @@ -0,0 +1,2 @@ +const BotClass = require('./classes/bot.class') +module.exports = { instance: BotClass } diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..77e12a7 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,21 @@ +{ + "name": "bot-core", + "version": "0.0.1", + "description": "", + "main": "index.js", + "private": true, + "scripts": { + "core:dev": "node ./index.js", + "core:rollup": "node ../../node_modules/.bin/rollup index.js --config ./rollup-cli.config.js", + "format:check": "prettier --check .", + "format:write": "prettier --write .", + "lint:check": "eslint .", + "lint:fix": "eslint --fix .", + "test:core": "node ../../node_modules/uvu/bin.js tests", + "build:core": "npm run format:write && npm run lint:fix && npm run io:rollup" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": {} +} diff --git a/packages/core/rollup-cli.config.js b/packages/core/rollup-cli.config.js new file mode 100644 index 0000000..9069686 --- /dev/null +++ b/packages/core/rollup-cli.config.js @@ -0,0 +1,10 @@ +const commonjs = require('@rollup/plugin-commonjs') + +module.exports = { + input: 'index.js', + output: { + file: 'lib/core/bundle.core.cjs', + format: 'cjs', + }, + plugins: [commonjs()], +} diff --git a/packages/core/tests/bot.class.test.js b/packages/core/tests/bot.class.test.js new file mode 100644 index 0000000..352dc24 --- /dev/null +++ b/packages/core/tests/bot.class.test.js @@ -0,0 +1,32 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const BotClass = require('../classes/bot.class') + +class MockDB { + saveLog = () => {} +} + +class MockProvider { + sendMessage = () => {} +} + +class MockFlow { + find = () => {} +} + +test(`BotClass emit ping`, () => { + let messages = [] + + const botBasic = new BotClass( + new MockFlow(), + new MockDB(), + new MockProvider() + ) + + botBasic.on('message', (ctx) => messages.push(ctx)) + botBasic.emit('message', 'ping') + + assert.is(messages.join(''), 'ping') +}) + +test.run() diff --git a/packages/index.js b/packages/index.js new file mode 100644 index 0000000..bcd72f3 --- /dev/null +++ b/packages/index.js @@ -0,0 +1,5 @@ +const inout = require('./io') +const provider = require('./provider') +const bot = require('./core') + +module.exports = { inout, provider, bot } diff --git a/packages/io/classes/flow.class.js b/packages/io/classes/flow.class.js new file mode 100644 index 0000000..fa0a173 --- /dev/null +++ b/packages/io/classes/flow.class.js @@ -0,0 +1,20 @@ +class FlowClass { + flow + constructor(_flow) { + this.flow = _flow + } + + find = (message, ref = false) => { + let keyRef = ref + let ansRef = null + if (!keyRef) { + keyRef = + this.flow.find((n) => n.keyword.includes(message))?.ref || null + } + ansRef = this.flow.find((n) => n.keyword === keyRef) + if (ansRef) return ansRef + return false + } +} + +module.exports = FlowClass diff --git a/packages/io/index.js b/packages/io/index.js index 4e5daf2..0dd0d22 100644 --- a/packages/io/index.js +++ b/packages/io/index.js @@ -1,4 +1,3 @@ const { addKeyword, addAnswer } = require('./methods') -module.exports = { addKeyword, addAnswer } - - +const FlowClass = require('./classes/flow.class') +module.exports = { addKeyword, addAnswer, instance: FlowClass } diff --git a/packages/io/package.json b/packages/io/package.json index 5848618..f4f865a 100644 --- a/packages/io/package.json +++ b/packages/io/package.json @@ -4,16 +4,6 @@ "description": "", "main": "index.js", "private": true, - "scripts": { - "io:dev": "node ./index.js", - "io:rollup": "node ../../node_modules/.bin/rollup index.js --config ./rollup-cli.config.js", - "format:check": "prettier --check .", - "format:write": "prettier --write .", - "lint:check": "eslint .", - "lint:fix": "eslint --fix .", - "test:io": "node ../../node_modules/uvu/bin.js tests", - "build:io": "npm run format:write && npm run lint:fix && npm run io:rollup" - }, "keywords": [], "author": "", "license": "ISC", diff --git a/packages/io/rollup-cli.config.js b/packages/io/rollup-cli.config.js index b2e6771..355242c 100644 --- a/packages/io/rollup-cli.config.js +++ b/packages/io/rollup-cli.config.js @@ -3,7 +3,7 @@ const commonjs = require('@rollup/plugin-commonjs') module.exports = { input: 'index.js', output: { - file: 'lib/io/bundle.cjs', + file: 'lib/io/bundle.io.cjs', format: 'cjs', }, plugins: [commonjs()], diff --git a/packages/provider/TODO.md b/packages/provider/TODO.md index 4efe110..1e2b948 100644 --- a/packages/provider/TODO.md +++ b/packages/provider/TODO.md @@ -6,9 +6,8 @@ const { inout, provider, database } = require('@bot-whatsapp') provider.start() provider.close() - ``` -- [ ] whatsapp-web.js _verificar update_ -- [ ] Meta _verificar tokens_ -- [ ] Twilio _verificar tokens_ \ No newline at end of file +- [ ] whatsapp-web.js _verificar update_ +- [ ] Meta _verificar tokens_ +- [ ] Twilio _verificar tokens_ diff --git a/packages/provider/classes/provider.class.js b/packages/provider/classes/provider.class.js new file mode 100644 index 0000000..5f58b79 --- /dev/null +++ b/packages/provider/classes/provider.class.js @@ -0,0 +1,9 @@ +class ProviderClass { + constructor() {} + + sendMessage = (message) => { + return message + } +} + +module.exports = ProviderClass diff --git a/packages/provider/index.js b/packages/provider/index.js new file mode 100644 index 0000000..e32e127 --- /dev/null +++ b/packages/provider/index.js @@ -0,0 +1,2 @@ +const ProviderClass = require('./classes/provider.class') +module.exports = { instance: ProviderClass } diff --git a/packages/provider/rollup-cli.config.js b/packages/provider/rollup-cli.config.js new file mode 100644 index 0000000..fa77e06 --- /dev/null +++ b/packages/provider/rollup-cli.config.js @@ -0,0 +1,10 @@ +const commonjs = require('@rollup/plugin-commonjs') + +module.exports = { + input: 'index.js', + output: { + file: 'lib/provider/bundle.provider.cjs', + format: 'cjs', + }, + plugins: [commonjs()], +} diff --git a/packages/rollup-cli.config.js b/packages/rollup-cli.config.js new file mode 100644 index 0000000..10aeedd --- /dev/null +++ b/packages/rollup-cli.config.js @@ -0,0 +1,10 @@ +const commonjs = require('@rollup/plugin-commonjs') + +module.exports = { + input: 'index.js', + output: { + file: 'lib/index.cjs', + format: 'cjs', + }, + plugins: [commonjs()], +}