TYPEBOT SEM VPS + NOVA API
1- INSTALAR UBUNTU WINDOWS STORE
2- PREPARA UBUNTU
3- PREPARAR DOCKER-COMPOSE.YML
a. docker-compose up -d
b. hostname -I
4- CONSTRUIR A API (GIT e NODE)
a. criar pasta e arquivos
b. npm install
c. node botzdg_typebot_api2
Ativar Ubuntu no Windows (WSL)
1- Abra o PowerShell como Administrador: Clique com o botão direito no menu Iniciar e escolha "Windows PowerShell (Admin)".
2- Habilite o recurso WSL: No PowerShell, execute o seguinte comando:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
3- Reinicie o Computador:
4- Instale uma Distribuição do Linux
5- Atualize para WSL 2 (Opcional):
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
Ubuntu
sudo apt update && sudo apt upgrade
sudo apt-add-repository universe
sudo apt install python2-minimal
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v
npm -v
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
sudo apt update
sudo apt install docker-ce
sudo systemctl status docker
sudo apt install docker-compose
sudo usermod -aG docker ${USER}
dockerd &
docker-compose.yml
version: '3.3'
services:
typebot-db:
image: postgres:13
restart: always
volumes:
- db_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=typebot
- POSTGRES_PASSWORD=typebot
typebot-builder:
ports:
- 3001:3000
image: baptistearno/typebot-builder:latest
restart: always
depends_on:
- typebot-db
environment:
- DATABASE_URL=postgresql://postgres:typebot@typebot-db:5432/typebot
- NEXTAUTH_URL=http://172.22.63.118:3001
- NEXT_PUBLIC_VIEWER_URL=http://172.22.63.118:3002
- ENCRYPTION_SECRET=9bd08a339fb03237e02cb5ce0f430675
- ADMIN_EMAIL=email@gmail.com
- SMTP_HOST=smtp.gmail.com
- SMTP_USERNAME=email@gmail.com
- SMTP_PASSWORD=senha
- NEXT_PUBLIC_SMTP_FROM='Suporte Typebot'
- S3_ACCESS_KEY=minio
- S3_SECRET_KEY=minio123
- S3_BUCKET=typebot
- S3_ENDPOINT=storagezdg.comunidadezdg.com.br
typebot-viewer:
ports:
- 3002:3000
image: baptistearno/typebot-viewer:latest
restart: always
environment:
- DATABASE_URL=postgresql://postgres:typebot@typebot-db:5432/typebot
- NEXT_PUBLIC_VIEWER_URL=http://172.22.63.118:3002
- NEXTAUTH_URL=http://172.22.63.118:3001
- ENCRYPTION_SECRET=9bd08a339fb03237e02cb5ce0f430675
- S3_ACCESS_KEY=minio
- S3_SECRET_KEY=minio123
- S3_BUCKET=typebot
- S3_ENDPOINT=storagezdg.comunidadezdg.com.br
mail:
image: bytemark/smtp
restart: always
minio:
labels:
virtual.host: 'storagezdg.comunidadezdg.com.br'
virtual.port: '9000'
virtual.tls-email: 'email@gmail.com'
image: minio/minio
command: server /data
ports:
- '9000:9000'
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio123
volumes:
- s3_data:/data
createbuckets:
image: minio/mc
depends_on:
- minio
entrypoint: >
/bin/sh -c "
sleep 10;
/usr/bin/mc config host add minio http://minio:9000 minio minio123;
/usr/bin/mc mb minio/typebot;
/usr/bin/mc anonymous set public minio/typebot/public;
exit 0;
"
volumes:
db_data:
s3_data:
index.html
WPP API by Pedrinho da NASA
package.json
{"name":"bot-zdg","version":"1.0.0","description":"bot-zdg: based on Whatsapp API","main":"app.js","scripts":{"start":"node .\botzdg_typebot_stop.js","start:dev":"nodemon app.js","test":"echo \"Error: no test specified\" && exit 1"},"keywords":["whatsapp-api","node.js"],"author":"Pedro","license":"MIT","dependencies":{"axios":"^1.5.0","express":"^4.17.1","express-fileupload":"^1.2.0","express-validator":"^6.9.2","http":"0.0.1-security","qrcode":"^1.4.4","qrcode-terminal":"^0.12.0","socket.io":"2.3.0","whatsapp-web.js":"^1.23.0"},"devDependencies":{"nodemon":"^2.0.19"}}
botzdg_typebot_api2.js
const { Client, LocalAuth, MessageMedia } = require('whatsapp-web.js');
const express = require('express');
const socketIO = require('socket.io');
const qrcode = require('qrcode');
const http = require('http');
const fileUpload = require('express-fileupload');
const port = 8000;
const app = express();
const server = http.createServer(app);
const io = socketIO(server);
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const url = 'http://172.22.63.118:3002/api/v1/';
const typebot = 'zdg';
const dirBot = './typebot';
if (!fs.existsSync(dirBot)){
fs.mkdirSync(dirBot)
}
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.use(fileUpload({
debug: true
}));
app.use("/", express.static(__dirname + "/"))
app.get('/', (req, res) => {
res.sendFile('index.html', {
root: __dirname
});
});
const client = new Client({
authStrategy: new LocalAuth({ clientId: 'typebot' }),
puppeteer: { headless: true,
executablePath: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--no-first-run',
'--no-zygote',
'--single-process', // <- this one doesn't works in Windows
'--disable-gpu'
] }
});
client.initialize();
io.on('connection', function(socket) {
socket.emit('message', '© BOT-ZDG - Iniciado');
socket.emit('qr', './icon.svg');
client.on('qr', (qr) => {
console.log('QR RECEIVED', qr);
qrcode.toDataURL(qr, (err, url) => {
socket.emit('qr', url);
socket.emit('message', '© BOT-ZDG QRCode recebido, aponte a câmera seu celular!');
});
});
client.on('ready', async () => {
socket.emit('ready', '© BOT-ZDG Dispositivo pronto!');
socket.emit('message', '© BOT-ZDG Dispositivo pronto!');
socket.emit('qr', './check.svg')
console.log('© BOT-ZDG Dispositivo pronto');
});
client.on('authenticated', () => {
socket.emit('authenticated', '© BOT-ZDG Autenticado!');
socket.emit('message', '© BOT-ZDG Autenticado!');
console.log('© BOT-ZDG Autenticado');
});
client.on('auth_failure', function() {
socket.emit('message', '© BOT-ZDG Falha na autenticação, reiniciando...');
console.error('© BOT-ZDG Falha na autenticação');
});
client.on('change_state', state => {
console.log('© BOT-ZDG Status de conexão: ', state );
});
client.on('disconnected', (reason) => {
socket.emit('message', '© BOT-ZDG Cliente desconectado!');
console.log('© BOT-ZDG Cliente desconectado', reason);
client.initialize();
});
});
function deleteDirectory(dirPath) {
if (fs.existsSync(dirPath)) {
const files = fs.readdirSync(dirPath);
files.forEach((file) => {
const filePath = path.join(dirPath, file);
if (fs.statSync(filePath).isDirectory()) {
deleteDirectory(filePath);
} else {
fs.unlinkSync(filePath);
}
});
try {
fs.rmdirSync(dirPath);
console.log(`Diretório ${dirPath} deletado com sucesso.`);
} catch (error) {
console.error(`Erro ao deletar diretório ${dirPath}:`, error);
}
} else {
console.log(`O diretório ${dirPath} não existe.`);
}
}
async function readWriteFileJson(sessionId, from) {
let dataFile = [];
fs.writeFileSync("./typebot/" + from + "/typebot.json", JSON.stringify(dataFile));
var data = fs.readFileSync("./typebot/" + from + "/typebot.json");
var myObject = JSON.parse(data);
let newData = {
id: sessionId,
};
await myObject.push(newData);
fs.writeFileSync("./typebot/" + from + "/typebot.json", JSON.stringify(myObject));
}
async function createSession(data){
let config = {
method: 'post',
maxBodyLength: Infinity,
url: `${url}typebots/${typebot}/startChat`,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
};
const response = await axios.request(config);
console.log('createSession' + response.data)
const dirFrom = './typebot/' + data.from.replace(/\D/g,'');
if (!fs.existsSync(dirFrom)){
fs.mkdirSync(dirFrom);
await readWriteFileJson(response.data.sessionId, data.from.replace(/\D/g,''));
}
return response.data
}
async function continueSession(data, msg){
let dataMessage = JSON.stringify({
"message": msg
});
let config = {
method: 'post',
maxBodyLength: Infinity,
url: `${url}sessions/${data}/continueChat`,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
data : dataMessage
};
const response = await axios.request(config);
console.log('continueSession', response.data)
return response.data
}
const timer = ms => new Promise(res => setTimeout(res, ms))
function randomIntFromInterval(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
const rndInt = randomIntFromInterval(1, 3)
client.on('message', async msg => {
const dirFrom = './typebot/' + msg.from.replace(/\D/g,'');
if (msg.body === 'sair'){
await deleteDirectory(dirFrom)
await client.sendMessage(msg.from, 'Atendimento automático reiniciado.')
}
if (msg.body !== 'sair'){
if (!fs.existsSync(dirFrom)){
const session = await createSession(msg);
const messages = session.messages
for (const message of messages){
if (message.type === 'text') {
let formattedText = '';
for (const richText of message.content.richText){
for (const element of richText.children){
let text = '';
if (element.text) {
text = element.text;
}
if (element.bold) {
text = `*${text}*`;
}
if (element.italic) {
text = `_${text}_`;
}
if (element.underline) {
text = `~${text}~`;
}
formattedText += text;
}
formattedText += '\n';
}
formattedText = formattedText.replace(/\n$/, '');
await client.sendMessage(msg.from, formattedText);
await timer(rndInt * 1000)
}
if (message.type === 'image' || message.type === 'video') {
try{
const media = await MessageMedia.fromUrl(message.content.url)
await client.sendMessage(msg.from, media, {caption: 'Comunidade ZDG'})
await timer(rndInt * 1000)
}catch(e){}
}
if (message.type === 'audio') {
try{
const media = await MessageMedia.fromUrl(message.content.url)
await client.sendMessage(msg.from, media, {sendAudioAsVoice: true})
await timer(rndInt * 1000)
}catch(e){}
}
}
const input = session.input
if (input) {
if (input.type === 'choice input') {
let formattedText = '';
const items = input.items;
for (const item of items) {
formattedText += `▶️ ${item.content}\n`;
}
formattedText = formattedText.replace(/\n$/, '');
await client.sendMessage(msg.from, formattedText);
await timer(rndInt * 1000)
}
}
}
else {
const from = msg.from.replace(/\D/g,'');
const sessionId = fs.readFileSync("./typebot/" + from + "/typebot.json","utf8").split(':')[1].replace(/\W/g, '');
const session = await continueSession(sessionId, msg.body);
const messages = session.messages
for (const message of messages){
if (message.type === 'text') {
let formattedText = '';
for (const richText of message.content.richText){
for (const element of richText.children){
let text = '';
if (element.text) {
text = element.text;
}
if (element.bold) {
text = `*${text}*`;
}
if (element.italic) {
text = `_${text}_`;
}
if (element.underline) {
text = `~${text}~`;
}
formattedText += text;
}
formattedText += '\n';
}
formattedText = formattedText.replace(/\n$/, '');
await client.sendMessage(msg.from, formattedText);
await timer(rndInt * 1000)
}
if (message.type === 'image' || message.type === 'video') {
try{
const media = await MessageMedia.fromUrl(message.content.url)
await client.sendMessage(msg.from, media, {caption: 'Comunidade ZDG'})
await timer(rndInt * 1000)
}catch(e){}
}
if (message.type === 'audio') {
try{
const media = await MessageMedia.fromUrl(message.content.url)
await client.sendMessage(msg.from, media, {sendAudioAsVoice: true})
await timer(rndInt * 1000)
}catch(e){}
}
}
const input = session.input
if (input) {
if (input.type === 'choice input') {
let formattedText = '';
const items = input.items;
for (const item of items) {
formattedText += `▶️ ${item.content}\n`;
}
formattedText = formattedText.replace(/\n$/, '');
await client.sendMessage(msg.from, formattedText);
await timer(rndInt * 1000)
}
}
}
}
});
server.listen(port, function() {
console.log('App running on *: ' + port);
});