├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── README.md
├── assets
├── address.png
├── assistant.png
├── bill.png
├── menu.png
├── order.png
└── welcome.png
├── package.json
├── src
├── menu.js
├── server.js
├── stages.js
├── stages
│ ├── 0.js
│ ├── 1.js
│ ├── 2.js
│ ├── 3.js
│ ├── 4.js
│ ├── 5.js
│ ├── index.js
│ └── neighborhoods.js
├── storage.js
└── venom.js
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@rocketseat/eslint-config/node"
4 | ]
5 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 | tokens
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Whatsapp Bot with VENOM-BOT
2 |
3 | ## Descrição do Projeto
4 |
5 |
6 |
10 | 🚀 Projeto criado com o intuito de auxiliar nas demandas de pedidos da empresa "Delícias da Neide" via WhatsApp.
11 |
12 |
13 | Welcome
14 |
15 | Menu
16 |
17 | Order
18 |
19 | Address
20 |
21 | Bill
22 |
23 | Assistant
24 |
25 |
26 |
27 | ### Pré-requisitos
28 |
29 | Antes de começar, você vai precisar ter instalado em sua máquina as seguintes ferramentas:
30 | [Git](https://git-scm.com), [Node.js](https://nodejs.org/en/).
31 | Além disto, é bom ter um editor para trabalhar com o código como [VSCode](https://code.visualstudio.com/).
32 |
33 | ### 🎲 Rodando nossa aplicação
34 |
35 | ```bash
36 | # Clone este repositório
37 | $ git clone git@github.com:juniorwmr/botwhatsapp-venom.git
38 |
39 | # Acesse a pasta do projeto no terminal/cmd
40 | $ cd botwhatsapp-venom
41 |
42 | # Instale as dependências
43 | $ npm install
44 |
45 | # Execute a aplicação em modo de desenvolvimento
46 | $ yarn dev
47 |
48 | ## Pronto, escaneie o código QR do Whatsapp e Voilà, aproveite!
49 | ```
50 |
51 | ### 🛠 Tecnologias
52 |
53 | As seguintes ferramentas foram usadas na construção do projeto:
54 |
55 | - [Node.js](https://nodejs.org/en/)
56 |
57 | ### Autor
58 |
59 | ---
60 |
61 |
62 |
63 |
64 |
65 | Done with ❤️ by Washington Muniz 👋🏽 !
66 |
67 | [](https://twitter.com/juniorwmr) [](https://www.linkedin.com/in/juniorwmr/)
68 | [](mailto:juniorripardo@gmail.com)
69 |
--------------------------------------------------------------------------------
/assets/address.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juniorwmr/botwhatsapp-venom/d01033177a37d153b566bb72bb2d7baf1865df9b/assets/address.png
--------------------------------------------------------------------------------
/assets/assistant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juniorwmr/botwhatsapp-venom/d01033177a37d153b566bb72bb2d7baf1865df9b/assets/assistant.png
--------------------------------------------------------------------------------
/assets/bill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juniorwmr/botwhatsapp-venom/d01033177a37d153b566bb72bb2d7baf1865df9b/assets/bill.png
--------------------------------------------------------------------------------
/assets/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juniorwmr/botwhatsapp-venom/d01033177a37d153b566bb72bb2d7baf1865df9b/assets/menu.png
--------------------------------------------------------------------------------
/assets/order.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juniorwmr/botwhatsapp-venom/d01033177a37d153b566bb72bb2d7baf1865df9b/assets/order.png
--------------------------------------------------------------------------------
/assets/welcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juniorwmr/botwhatsapp-venom/d01033177a37d153b566bb72bb2d7baf1865df9b/assets/welcome.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "botwhatsapp",
3 | "type": "module",
4 | "version": "1.0.0",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "scripts": {
8 | "start": "node src/server.js",
9 | "dev": "nodemon src/server.js"
10 | },
11 | "devDependencies": {
12 | "@rocketseat/eslint-config": "^1.2.0",
13 | "nodemon": "^2.0.22"
14 | },
15 | "dependencies": {
16 | "venom-bot": "^5.0.20"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/menu.js:
--------------------------------------------------------------------------------
1 | export const menu = {
2 | 1: {
3 | description: 'Leite Ninho',
4 | price: 6,
5 | },
6 | 2: {
7 | description: 'Alpino',
8 | price: 6,
9 | },
10 | 3: {
11 | description: 'Prestígio',
12 | price: 6,
13 | },
14 | 4: {
15 | description: 'Abacaxi',
16 | price: 6,
17 | },
18 | 5: {
19 | description: 'Dois Amores',
20 | price: 6,
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/src/server.js:
--------------------------------------------------------------------------------
1 | import { VenomBot } from './venom.js'
2 | import { stages, getStage } from './stages.js'
3 |
4 | const main = async () => {
5 | try {
6 | const venombot = await VenomBot.getInstance().init({
7 | session: 'Delícias da Neide',
8 | headless: true,
9 | useChrome: false,
10 | })
11 |
12 | venombot.onMessage(async (message) => {
13 | if (message.isGroupMsg) return
14 |
15 | const currentStage = getStage({ from: message.from })
16 |
17 | await stages[currentStage].stage.exec({
18 | from: message.from,
19 | message: message.body,
20 | })
21 | })
22 | } catch (error) {
23 | console.error(error)
24 | }
25 | }
26 |
27 | main()
28 |
--------------------------------------------------------------------------------
/src/stages.js:
--------------------------------------------------------------------------------
1 | import {
2 | initialStage,
3 | stageOne,
4 | stageTwo,
5 | stageThree,
6 | stageFour,
7 | finalStage,
8 | } from './stages/index.js'
9 |
10 | import { storage } from './storage.js'
11 |
12 | export const stages = [
13 | {
14 | descricao: 'Welcome',
15 | stage: initialStage,
16 | },
17 | {
18 | descricao: 'Menu',
19 | stage: stageOne,
20 | },
21 | {
22 | descricao: 'Address',
23 | stage: stageTwo,
24 | },
25 | {
26 | descricao: 'Bill',
27 | stage: stageThree,
28 | },
29 | {
30 | descricao: 'New Order',
31 | stage: stageFour,
32 | },
33 | {
34 | descricao: 'Assistent',
35 | stage: finalStage,
36 | },
37 | ]
38 |
39 | export function getStage({ from }) {
40 | if (storage[from]) {
41 | return storage[from].stage
42 | }
43 |
44 | storage[from] = {
45 | stage: 0,
46 | itens: [],
47 | address: '',
48 | }
49 |
50 | return storage[from].stage
51 | }
52 |
--------------------------------------------------------------------------------
/src/stages/0.js:
--------------------------------------------------------------------------------
1 | import { storage } from '../storage.js'
2 | import { VenomBot } from '../venom.js'
3 | import { STAGES } from './index.js'
4 |
5 | export const initialStage = {
6 | async exec({ from }) {
7 | storage[from].stage = STAGES.MENU
8 |
9 | const venombot = await VenomBot.getInstance()
10 |
11 | const message = `
12 | 👋 Olá, como vai?
13 | Eu sou Carlos, o *assistente virtual* da ${venombot.getSessionName}.
14 | *Posso te ajudar?* 🙋♂️
15 | -----------------------------------
16 | 1️⃣ - FAZER PEDIDO
17 | 2️⃣ - VERIFICAR TAXA DE ENTREGA
18 | 0️⃣ - FALAR COM ATENDENTE
19 | `
20 | await venombot.sendText({ to: from, message })
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/src/stages/1.js:
--------------------------------------------------------------------------------
1 | import { VenomBot } from '../venom.js'
2 | import { menu } from '../menu.js'
3 | import { storage } from '../storage.js'
4 | import { neighborhoods } from './neighborhoods.js'
5 | import { initialStage } from './0.js'
6 | import { STAGES } from './index.js'
7 |
8 | export const stageOne = {
9 | async exec(params) {
10 | const message = params.message.trim()
11 | const isMsgValid = /[0|1|2]/.test(message)
12 |
13 | let msg =
14 | '❌ *Digite uma opção válida, por favor.* \n⚠️ ```APENAS UMA OPÇÃO POR VEZ``` ⚠️'
15 |
16 | if (isMsgValid) {
17 | const option = options[Number(message)]()
18 | msg = option.message
19 | storage[params.from].stage = option.nextStage || STAGES.INICIAL
20 | }
21 |
22 | await VenomBot.getInstance().sendText({ to: params.from, message: msg })
23 |
24 | if (storage[params.from].stage === STAGES.INICIAL) {
25 | await initialStage.exec(params)
26 | } else if (storage[params.from].stage === STAGES.FALAR_COM_ATENDENTE) {
27 | storage[params.from].finalStage = {
28 | startsIn: new Date().getTime(),
29 | endsIn: new Date().setSeconds(60), // 1 minute of inactivity
30 | }
31 | }
32 | },
33 | }
34 |
35 | const options = {
36 | 1: () => {
37 | let message = '🚨 CARDÁPIO 🚨\n\n'
38 |
39 | Object.keys(menu).forEach((value) => {
40 | message += `${numbers[value]} - _${menu[value].description}_ \n`
41 | })
42 |
43 | return {
44 | message,
45 | nextStage: STAGES.CARRINHO,
46 | }
47 | },
48 | 2: () => {
49 | const message =
50 | '\n-----------------------------------\n1️⃣ - ```FAZER PEDIDO``` \n0️⃣ - ```FALAR COM ATENDENTE```\n\n' +
51 | neighborhoods +
52 | '\n-----------------------------------\n1️⃣ - ```FAZER PEDIDO``` \n0️⃣ - ```FALAR COM ATENDENTE``` '
53 |
54 | return {
55 | message,
56 | nextStage: null,
57 | }
58 | },
59 | 0: () => {
60 | return {
61 | message:
62 | '🔃 Encaminhando você para um atendente. \n⏳ *Aguarde um instante*.\n \n⚠️ A qualquer momento, digite *ENCERRAR* para encerrar o atendimento. ⚠️',
63 | nextStage: STAGES.FALAR_COM_ATENDENTE,
64 | }
65 | },
66 | }
67 |
68 | const numbers = {
69 | 1: '1️⃣',
70 | 2: '2️⃣',
71 | 3: '3️⃣',
72 | 4: '4️⃣',
73 | 5: '5️⃣',
74 | }
75 |
--------------------------------------------------------------------------------
/src/stages/2.js:
--------------------------------------------------------------------------------
1 | import { VenomBot } from '../venom.js'
2 | import { menu } from '../menu.js'
3 | import { storage } from '../storage.js'
4 | import { STAGES } from './index.js'
5 |
6 | export const stageTwo = {
7 | async exec(params) {
8 | const message = params.message.trim()
9 | const isMsgValid = /[1|2|3|4|5|#|*]/.test(message)
10 |
11 | let msg =
12 | '❌ *Digite uma opção válida, por favor.* \n⚠️ ```APENAS UMA OPÇÃO POR VEZ``` ⚠️'
13 |
14 | if (isMsgValid) {
15 | if (['#', '*'].includes(message)) {
16 | const option = options[message]()
17 | msg = option.message
18 | storage[params.from].stage = option.nextStage
19 | } else {
20 | msg =
21 | `✅ *${menu[message].description}* adicionado com sucesso! \n\n` +
22 | '```Digite outra opção```: \n\n' +
23 | '\n-----------------------------------\n#️⃣ - ```FINALIZAR pedido``` \n*️⃣ - ```CANCELAR pedido```'
24 | storage[params.from].itens.push(menu[message])
25 | }
26 |
27 | if (storage[params.from].stage === STAGES.INICIAL) {
28 | storage[params.from].itens = []
29 | }
30 | }
31 |
32 | await VenomBot.getInstance().sendText({ to: params.from, message: msg })
33 | },
34 | }
35 |
36 | const options = {
37 | '*': () => {
38 | const message =
39 | '🔴 Pedido *CANCELADO* com sucesso. \n\n ```Volte Sempre!```'
40 |
41 | return {
42 | message,
43 | nextStage: STAGES.INICIAL,
44 | }
45 | },
46 | '#': () => {
47 | const message =
48 | '🗺️ Agora, informe o *ENDEREÇO*. \n ( ```Rua, Número, Bairro``` ) \n\n ' +
49 | '\n-----------------------------------\n*️⃣ - ```CANCELAR pedido```'
50 |
51 | return {
52 | message,
53 | nextStage: STAGES.RESUMO,
54 | }
55 | },
56 | }
57 |
--------------------------------------------------------------------------------
/src/stages/3.js:
--------------------------------------------------------------------------------
1 | import { VenomBot } from '../venom.js'
2 | import { storage } from '../storage.js'
3 | import { STAGES } from './index.js'
4 |
5 | export const stageThree = {
6 | async exec({ from, message }) {
7 | storage[from].address = message
8 | storage[from].stage = STAGES.PEDIDO
9 |
10 | let msg = 'Pedido *CANCELADO* com sucesso. \n Volte Sempre!'
11 | if (message === '*') {
12 | storage[from].stage = STAGES.INICIAL
13 | } else {
14 | const itens = storage[from].itens
15 | const desserts = itens.map((item) => item.description).join(', ')
16 |
17 | const total = storage[from].itens.length
18 |
19 | msg =
20 | `🗒️ *RESUMO DO PEDIDO*: \n\n🧁 Sabores: *${desserts}* \n🚚 Taxa de entrega: *a confirmar*. \n📍 Endereço: *${message}* \n💰 Valor dos bolos: *${
21 | total * 6
22 | },00 reais*. \n⏳ Tempo de entrega: *50 minutos*. \n\n` +
23 | '🔊 ```Agora, informe a forma de pagamento e se vai precisar de troco, por gentileza.```'
24 | }
25 |
26 | await VenomBot.getInstance().sendText({ to: from, message: msg })
27 |
28 | // return '✅ *Prontinho, pedido feito!* \n\nAgora, se você ainda não sabe o valor da taxa de entrega para sua região, vou te passar para um atendente para que ele verique o valor da *taxa de entrega*. \n\n⏳ *Aguarde um instante*.'
29 | },
30 | }
31 |
--------------------------------------------------------------------------------
/src/stages/4.js:
--------------------------------------------------------------------------------
1 | import { VenomBot } from '../venom.js'
2 | import { storage } from '../storage.js'
3 | import { STAGES } from './index.js'
4 |
5 | export const stageFour = {
6 | async exec({ from, message }) {
7 | const address = storage[from].address
8 | const phone = from.split('@')
9 |
10 | storage[from].stage = STAGES.FALAR_COM_ATENDENTE
11 |
12 | storage[from].finalStage = {
13 | startsIn: new Date().getTime(),
14 | endsIn: new Date().setSeconds(60), // 1 minute of inactivity
15 | }
16 |
17 | const itens = storage[from].itens
18 | const desserts = itens.map((item) => item.description).join(', ')
19 | const total = storage[from].itens.length
20 |
21 | const msg = `🔔 *NOVO PEDIDO* 🔔: \n\n📞 Cliente: +${
22 | phone[0]
23 | } \n🧁 Sabores: *${desserts}* \n📍 Endereço: *${address}* \n🚚 Taxa de entrega: *a confirmar*. \n💰 Valor dos bolos: *${
24 | total * 6
25 | },00 reais*. \n⏳ Tempo de entrega: *50 minutos*. \n🛑 Detalhes: *${message}*`
26 |
27 | await VenomBot.getInstance().sendText({ to: from, message: msg })
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/src/stages/5.js:
--------------------------------------------------------------------------------
1 | import { VenomBot } from '../venom.js'
2 | import { storage } from '../storage.js'
3 | import { STAGES } from './index.js'
4 |
5 | export const finalStage = {
6 | async exec({ from, message }) {
7 | const msg = message.trim().toUpperCase()
8 |
9 | const currentDate = new Date()
10 | const history = storage[from].finalStage
11 |
12 | if (history.endsIn < currentDate.getTime() || msg === 'ENCERRAR') {
13 | storage[from].stage = STAGES.INICIAL
14 | return VenomBot.getInstance().sendText({
15 | to: from,
16 | message: '🔚 *Atendimento encerrado* 🔚',
17 | })
18 | }
19 |
20 | storage[from].finalStage.endsIn = new Date().setSeconds(60) // more 1 minute of inactivity
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/src/stages/index.js:
--------------------------------------------------------------------------------
1 | export * from './0.js'
2 | export * from './1.js'
3 | export * from './2.js'
4 | export * from './3.js'
5 | export * from './4.js'
6 | export * from './5.js'
7 |
8 | export const STAGES = {
9 | INICIAL: '0',
10 | MENU: '1',
11 | CARRINHO: '2',
12 | RESUMO: '3',
13 | PEDIDO: '4',
14 | FALAR_COM_ATENDENTE: '5',
15 | }
16 |
--------------------------------------------------------------------------------
/src/stages/neighborhoods.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-multi-str */
2 | export const neighborhoods =
3 | 'Abraão Alab --> *5 reais* \
4 | \nAdalberto Aragão --> *5 reais* \
5 | \nAeroporto Velho --> *5 reais* \
6 | \nAreial --> *6 reais* \
7 | \nAviário --> *5 reais* \
8 | \nAyrton Senna --> *7 reais* \
9 | \nBahia Nova --> *7 reais* \
10 | \nBahia Velha --> *7 reais* \
11 | \nBairro XV --> *5 reais* \
12 | \nBairro da Base --> *5 reais* \
13 | \nBairro da Paz --> *5 reais* \
14 | \nBoa União --> *7 reais* \
15 | \nBosque --> *5 reais* \
16 | \nCadeia Velha --> *5 reais* \
17 | \nCaladinho --> *7 reais* \
18 | \nCalafate --> *8 reais* \
19 | \nCapoeira --> *5 reais* \
20 | \nCentro --> *5 reais* \
21 | \nCerâmica --> *5 reais* \
22 | \nChico Mendes --> *5 reais* \
23 | \nCidade Nova --> *5 reais* \
24 | \nConjunto Adalberto Sena --> *5 reais* \
25 | \nConjunto Bela Vista --> *5 reais* \
26 | \nConjunto Castelo Branco --> *5 reais* \
27 | \nConjunto Esperança I e II --> *5 reais* \
28 | \nConjunto Guiomard Santos --> *5 reais* \
29 | \nConjunto Mariana --> *6 reais* \
30 | \nConjunto Mascarenha de Morais --> *5 reais* \
31 | \nConjunto Rui Lino --> *6 reais* \
32 | \nConjunto Tancredo Neves --> *5 reais* \
33 | \nConjunto Tangará --> *5 reais* \
34 | \nConjunto Tucumã I --> *7 reais* \
35 | \nConjunto Tucumã II --> *7 reais* \
36 | \nConjunto Xavier Maia --> *5 reais* \
37 | \nConquista --> *5 reais* \
38 | \nDefesa Civil --> *5 reais* \
39 | \nDoca Furtado --> *5 reais* \
40 | \nEldorado --> *5 reais* \
41 | \nEstação Experimental --> *5 reais* \
42 | \nFloresta --> *6 reais* \
43 | \nFloresta Sul --> *8 reais* \
44 | \nGeraldo Fleming --> *5 reais* \
45 | \nHabitasa --> *6 reais* \
46 | \nIpase --> *5 reais* \
47 | \nIpê --> *8 reais* \
48 | \nIrineu Serra --> *7 reais* \
49 | \nIvete Vargas --> *5 reais* \
50 | \nIsaura Parente --> *5 reais* \
51 | \nJardim Brasil --> *5 reais* \
52 | \nJardim Europa --> *5 reais* \
53 | \nJardim Primavera --> *6 reais* \
54 | \nJoão Eduardo I --> *5 reais* \
55 | \nJoão Eduardo II --> *5 reais*\
56 | \nJorge Lavocat --> *5 reais* \
57 | \nLoteamento Novo Horizonte --> *5 reais* \
58 | \nManoel Julião --> *5 reais* \
59 | \nMocinha Magalhães --> *7 reais* \
60 | \nMontanhês --> *5 reais* \
61 | \nMorada do Sol --> *5 reais* \
62 | \nNova Estação --> *5 reais* \
63 | \nPalheiral --> *6 reais* \
64 | \nPapouco --> *5 reais* \
65 | \nParque dos Sabiás --> *5 reais* \
66 | \nPista --> *5 reais* \
67 | \nPlacas --> *5 reais* \
68 | \nPortal da Amazônia Placas --> *8 reais* \
69 | \nRaimundo Melo --> *5 reais* \
70 | \nResidencial Ouricuri --> *5 reais* \
71 | \nSanta Inês --> *8 reais* \
72 | \nSão Francisco --> *5 reais* \
73 | \nSeis de Agosto --> *5 reais* \
74 | \nSobral --> *5 reais* \
75 | \nTropical --> *5 reais* \
76 | \nVila Ivonete --> *5 reais* \
77 | \nVila Nova --> *5 reais* \
78 | \nVillage --> *5 reais* \
79 | \nVitória --> *5 reais* \
80 | \nVolta Seca --> *5 reais* \
81 | \nWanderley Dantas --> *5 reais*'
82 |
--------------------------------------------------------------------------------
/src/storage.js:
--------------------------------------------------------------------------------
1 | export const storage = Object.create({})
2 |
--------------------------------------------------------------------------------
/src/venom.js:
--------------------------------------------------------------------------------
1 | import { create } from 'venom-bot'
2 |
3 | export class VenomBot {
4 | #venombot
5 |
6 | #session
7 |
8 | static getInstance() {
9 | if (VenomBot.instance === undefined) VenomBot.instance = new VenomBot()
10 | return VenomBot.instance
11 | }
12 |
13 | async init({ session, headless, useChrome }) {
14 | this.#session = session
15 | this.#venombot = await create({
16 | session,
17 | headless,
18 | useChrome,
19 | multidevice: false,
20 | })
21 |
22 | return this
23 | }
24 |
25 | get getSessionName() {
26 | return this.#session
27 | }
28 |
29 | async onMessage(callback) {
30 | if (!this.#venombot) throw new Error('VenomBot not initialized.')
31 | return await this.#venombot.onMessage(callback)
32 | }
33 |
34 | async sendText({ to, message }) {
35 | if (!this.#venombot) throw new Error('VenomBot not initialized.')
36 | return await this.#venombot.sendText(to, message)
37 | }
38 |
39 | // Is not working
40 | // async sendButtons({ to, title, buttons, description }) {
41 | // if (!this.#venombot) throw new Error('VenomBot not initialized.')
42 |
43 | // return await this.#venombot.sendButtons(
44 | // to,
45 | // title,
46 | // buttons,
47 | // description,
48 | // )
49 | // }
50 |
51 | async markUnseenMessage({ to }) {
52 | if (!this.#venombot) throw new Error('VenomBot not initialized.')
53 | return await this.#venombot.markUnseenMessage(to)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------