From 908d5be53f1a444107a13ca503a462f4c9cdb87e Mon Sep 17 00:00:00 2001 From: Pedro Henrique Date: Sat, 18 Jan 2025 18:03:50 -0300 Subject: [PATCH] first commit --- .DS_Store | Bin 6148 -> 6148 bytes .env | 281 ++++++++++++++++++ .gitignore | 1 - Docker/scripts/generate_database.sh | 1 - Dockerfile | 5 +- docker-compose.yaml | 9 +- package.json | 2 +- prisma/postgresql-schema.prisma | 5 + .../whatsapp/whatsapp.baileys.service.ts | 4 +- src/main.ts | 23 ++ 10 files changed, 320 insertions(+), 11 deletions(-) create mode 100644 .env diff --git a/.DS_Store b/.DS_Store index f4f05c71662ef24da6e8eea7dbc5916385a9163a..3ccbdbaa551a95e85817caa3724e4d477cccea4c 100644 GIT binary patch delta 27 jcmZoMXfc@J&nUSuU^g?P}DPoedc;rhBAgshE#@fhT@!b!{Frn+yVwL5HVC6*OIf2I-;vcA)ull1K9=xp!SI=zMI)O{_+C=vOy-5 diff --git a/.env b/.env new file mode 100644 index 00000000..6419ce27 --- /dev/null +++ b/.env @@ -0,0 +1,281 @@ +SERVER_TYPE=http +SERVER_PORT=8080 +# Server URL - Set your application url +SERVER_URL=http://host.docker.internal:8080 + +SENTRY_DSN= + +# Cors - * for all or set separate by commas - ex.: 'yourdomain1.com, yourdomain2.com' +CORS_ORIGIN=* +CORS_METHODS=GET,POST,PUT,DELETE +CORS_CREDENTIALS=true + +# Determine the logs to be displayed +LOG_LEVEL=ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS,WEBSOCKET +LOG_COLOR=true +# Log Baileys - "fatal" | "error" | "warn" | "info" | "debug" | "trace" +LOG_BAILEYS=error + +# Set the maximum number of listeners that can be registered for an event +EVENT_EMITTER_MAX_LISTENERS=50 + +# Determine how long the instance should be deleted from memory in case of no connection. +# Default time: 5 minutes +# If you don't even want an expiration, enter the value false +DEL_INSTANCE=false + +# Provider: postgresql | mysql +DATABASE_PROVIDER=postgresql +DATABASE_CONNECTION_URI='postgresql://postgres:kajsdnfjknsadkfjnkasd154asd@postgres:5432/evolution?schema=public' +# DATABASE_CONNECTION_URI='postgresql://postgres:kajsdnfjknsadkfjnkasd154asd@localhost:5433/evolution?schema=public' +# Client name for the database connection +# It is used to separate an API installation from another that uses the same database. +DATABASE_CONNECTION_CLIENT_NAME=evolution_exchange + +# Choose the data you want to save in the application's database +DATABASE_SAVE_DATA_INSTANCE=true +DATABASE_SAVE_DATA_NEW_MESSAGE=true +DATABASE_SAVE_MESSAGE_UPDATE=true +DATABASE_SAVE_DATA_CONTACTS=true +DATABASE_SAVE_DATA_CHATS=true +DATABASE_SAVE_DATA_LABELS=true +DATABASE_SAVE_DATA_HISTORIC=true +DATABASE_SAVE_IS_ON_WHATSAPP=true +DATABASE_SAVE_IS_ON_WHATSAPP_DAYS=7 +DATABASE_DELETE_MESSAGE=true + +# RabbitMQ - Environment variables +RABBITMQ_ENABLED=false +RABBITMQ_URI=amqp://localhost +RABBITMQ_EXCHANGE_NAME=evolution +# Global events - By enabling this variable, events from all instances are sent in the same event queue. +RABBITMQ_GLOBAL_ENABLED=false +# Choose the events you want to send to RabbitMQ +RABBITMQ_EVENTS_APPLICATION_STARTUP=false +RABBITMQ_EVENTS_INSTANCE_CREATE=false +RABBITMQ_EVENTS_INSTANCE_DELETE=false +RABBITMQ_EVENTS_QRCODE_UPDATED=false +RABBITMQ_EVENTS_MESSAGES_SET=false +RABBITMQ_EVENTS_MESSAGES_UPSERT=false +RABBITMQ_EVENTS_MESSAGES_EDITED=false +RABBITMQ_EVENTS_MESSAGES_UPDATE=false +RABBITMQ_EVENTS_MESSAGES_DELETE=false +RABBITMQ_EVENTS_SEND_MESSAGE=false +RABBITMQ_EVENTS_CONTACTS_SET=false +RABBITMQ_EVENTS_CONTACTS_UPSERT=false +RABBITMQ_EVENTS_CONTACTS_UPDATE=false +RABBITMQ_EVENTS_PRESENCE_UPDATE=false +RABBITMQ_EVENTS_CHATS_SET=false +RABBITMQ_EVENTS_CHATS_UPSERT=false +RABBITMQ_EVENTS_CHATS_UPDATE=false +RABBITMQ_EVENTS_CHATS_DELETE=false +RABBITMQ_EVENTS_GROUPS_UPSERT=false +RABBITMQ_EVENTS_GROUP_UPDATE=false +RABBITMQ_EVENTS_GROUP_PARTICIPANTS_UPDATE=false +RABBITMQ_EVENTS_CONNECTION_UPDATE=false +RABBITMQ_EVENTS_REMOVE_INSTANCE=false +RABBITMQ_EVENTS_LOGOUT_INSTANCE=false +RABBITMQ_EVENTS_CALL=false +RABBITMQ_EVENTS_TYPEBOT_START=false +RABBITMQ_EVENTS_TYPEBOT_CHANGE_STATUS=false + +# SQS - Environment variables +SQS_ENABLED=false +SQS_ACCESS_KEY_ID= +SQS_SECRET_ACCESS_KEY= +SQS_ACCOUNT_ID= +SQS_REGION= + +# Websocket - Environment variables +WEBSOCKET_ENABLED=false +WEBSOCKET_GLOBAL_EVENTS=false + +# Pusher - Environment variables +PUSHER_ENABLED=false +PUSHER_GLOBAL_ENABLED=false +PUSHER_GLOBAL_APP_ID= +PUSHER_GLOBAL_KEY= +PUSHER_GLOBAL_SECRET= +PUSHER_GLOBAL_CLUSTER= +PUSHER_GLOBAL_USE_TLS=true +# Choose the events you want to send to Pusher +PUSHER_EVENTS_APPLICATION_STARTUP=true +PUSHER_EVENTS_QRCODE_UPDATED=true +PUSHER_EVENTS_MESSAGES_SET=true +PUSHER_EVENTS_MESSAGES_UPSERT=true +PUSHER_EVENTS_MESSAGES_EDITED=true +PUSHER_EVENTS_MESSAGES_UPDATE=true +PUSHER_EVENTS_MESSAGES_DELETE=true +PUSHER_EVENTS_SEND_MESSAGE=true +PUSHER_EVENTS_CONTACTS_SET=true +PUSHER_EVENTS_CONTACTS_UPSERT=true +PUSHER_EVENTS_CONTACTS_UPDATE=true +PUSHER_EVENTS_PRESENCE_UPDATE=true +PUSHER_EVENTS_CHATS_SET=true +PUSHER_EVENTS_CHATS_UPSERT=true +PUSHER_EVENTS_CHATS_UPDATE=true +PUSHER_EVENTS_CHATS_DELETE=true +PUSHER_EVENTS_GROUPS_UPSERT=true +PUSHER_EVENTS_GROUPS_UPDATE=true +PUSHER_EVENTS_GROUP_PARTICIPANTS_UPDATE=true +PUSHER_EVENTS_CONNECTION_UPDATE=true +PUSHER_EVENTS_LABELS_EDIT=true +PUSHER_EVENTS_LABELS_ASSOCIATION=true +PUSHER_EVENTS_CALL=true +PUSHER_EVENTS_TYPEBOT_START=false +PUSHER_EVENTS_TYPEBOT_CHANGE_STATUS=false + +# WhatsApp Business API - Environment variables +# Token used to validate the webhook on the Facebook APP +WA_BUSINESS_TOKEN_WEBHOOK=evolution +WA_BUSINESS_URL=https://graph.facebook.com +WA_BUSINESS_VERSION=v20.0 +WA_BUSINESS_LANGUAGE=en_US + +# Global Webhook Settings +# Each instance's Webhook URL and events will be requested at the time it is created +WEBHOOK_GLOBAL_ENABLED=false +# Define a global webhook that will listen for enabled events from all instances +WEBHOOK_GLOBAL_URL='' +# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event +WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false +# Set the events you want to hear +WEBHOOK_EVENTS_APPLICATION_STARTUP=false +WEBHOOK_EVENTS_QRCODE_UPDATED=true +WEBHOOK_EVENTS_MESSAGES_SET=false +WEBHOOK_EVENTS_MESSAGES_UPSERT=true +WEBHOOK_EVENTS_MESSAGES_EDITED=true +WEBHOOK_EVENTS_MESSAGES_UPDATE=true +WEBHOOK_EVENTS_MESSAGES_DELETE=true +WEBHOOK_EVENTS_SEND_MESSAGE=true +WEBHOOK_EVENTS_CONTACTS_SET=true +WEBHOOK_EVENTS_CONTACTS_UPSERT=true +WEBHOOK_EVENTS_CONTACTS_UPDATE=true +WEBHOOK_EVENTS_PRESENCE_UPDATE=true +WEBHOOK_EVENTS_CHATS_SET=true +WEBHOOK_EVENTS_CHATS_UPSERT=true +WEBHOOK_EVENTS_CHATS_UPDATE=true +WEBHOOK_EVENTS_CHATS_DELETE=true +WEBHOOK_EVENTS_GROUPS_UPSERT=true +WEBHOOK_EVENTS_GROUPS_UPDATE=true +WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true +WEBHOOK_EVENTS_CONNECTION_UPDATE=true +WEBHOOK_EVENTS_REMOVE_INSTANCE=false +WEBHOOK_EVENTS_LOGOUT_INSTANCE=false +WEBHOOK_EVENTS_LABELS_EDIT=true +WEBHOOK_EVENTS_LABELS_ASSOCIATION=true +WEBHOOK_EVENTS_CALL=true +# This events is used with Typebot +WEBHOOK_EVENTS_TYPEBOT_START=false +WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS=false +# This event is used to send errors +WEBHOOK_EVENTS_ERRORS=false +WEBHOOK_EVENTS_ERRORS_WEBHOOK= + +# Name that will be displayed on smartphone connection +CONFIG_SESSION_PHONE_CLIENT=Evolution +# Browser Name = Chrome | Firefox | Edge | Opera | Safari +CONFIG_SESSION_PHONE_NAME=Chrome + +# Whatsapp Web version for baileys channel +# https://web.whatsapp.com/check-update?version=0&platform=web +CONFIG_SESSION_PHONE_VERSION=2.3000.1015901307 + +# Set qrcode display limit +QRCODE_LIMIT=30 +# Color of the QRCode on base64 +QRCODE_COLOR='#175197' + +# Typebot - Environment variables +TYPEBOT_ENABLED=false +# old | latest +TYPEBOT_API_VERSION=latest + +# Chatwoot - Environment variables +CHATWOOT_ENABLED=true +# If you leave this option as false, when deleting the message for everyone on WhatsApp, it will not be deleted on Chatwoot. +CHATWOOT_MESSAGE_READ=true +# If you leave this option as true, when sending a message in Chatwoot, the client's last message will be marked as read on WhatsApp. +CHATWOOT_MESSAGE_DELETE=true +# If you leave this option as true, a contact will be created on Chatwoot to provide the QR Code and update messages about the instance. +CHATWOOT_BOT_CONTACT=true +# This db connection is used to import messages from whatsapp to chatwoot database +CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgresql://postgres:postgres@host.docker.internal:5434/chatwoot?sslmode=disable +# CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgresql://postgres:postgres@localhost:5434/chatwoot?sslmode=disable +CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE=true + +# OpenAI - Environment variables +OPENAI_ENABLED=false + +# Dify - Environment variables +DIFY_ENABLED=false + +# Cache - Environment variables +# Redis Cache enabled +CACHE_REDIS_ENABLED=true +CACHE_REDIS_URI=redis://redis:6379/6 +CACHE_REDIS_TTL=604800 +# Prefix serves to differentiate data from one installation to another that are using the same redis +CACHE_REDIS_PREFIX_KEY=evolution +# Enabling this variable will save the connection information in Redis and not in the database. +CACHE_REDIS_SAVE_INSTANCES=false +# Local Cache enabled +CACHE_LOCAL_ENABLED=false + +# Amazon S3 - Environment variables +S3_ENABLED=false +S3_ACCESS_KEY= +S3_SECRET_KEY= +S3_BUCKET=evolution +S3_PORT=443 +S3_ENDPOINT=s3.domain.com +S3_REGION=eu-west-3 +S3_USE_SSL=true + +# AMAZON S3 - Environment variables +# S3_ENABLED=true +# S3_BUCKET=bucket_name +# S3_ACCESS_KEY=access_key_id +# S3_SECRET_KEY=secret_access_key +# S3_ENDPOINT=s3.amazonaws.com # region: s3.eu-west-3.amazonaws.com +# S3_REGION=eu-west-3 + +# MINIO Use SSL - Environment variables +# S3_ENABLED=true +# S3_ACCESS_KEY=access_key_id +# S3_SECRET_KEY=secret_access_key +# S3_BUCKET=bucket_name +# S3_PORT=443 +# S3_ENDPOINT=s3.domain.com +# S3_USE_SSL=true +# S3_REGION=eu-south + +# Evolution Audio Converter - Environment variables - https://github.com/EvolutionAPI/evolution-audio-converter +# API_AUDIO_CONVERTER=http://localhost:4040/process-audio +# API_AUDIO_CONVERTER_KEY=429683C4C977415CAAFCCE10F7D57E11 + +# Define a global apikey to access all instances. +# OBS: This key must be inserted in the request header to create an instance. + +AUTHENTICATION_TYPE=apikey +AUTHENTICATION_API_KEY=429683C4C977415CAAFCCE10F7D57E11 + +# If you leave this option as true, the instances will be exposed in the fetch instances endpoint. +AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true +LANGUAGE=en + +# Define a global proxy to be used if the instance does not have one +# PROXY_HOST= +# PROXY_PORT=80 +# PROXY_PROTOCOL=http +# PROXY_USERNAME= +# PROXY_PASSWORD= + +STORE_CHATS=true +CLEAN_STORE_CLEANING_INTERVAL=7200 # seconds === 2h +CLEAN_STORE_MESSAGES=true +CLEAN_STORE_MESSAGE_UP=true +CLEAN_STORE_CONTACTS=true +CLEAN_STORE_CHATS=true + +QRCODE_LIMIT=30 diff --git a/.gitignore b/.gitignore index 3c511120..97999f3b 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,6 @@ lerna-debug.log* /test/ /src/env.yml /store -*.env /temp/* diff --git a/Docker/scripts/generate_database.sh b/Docker/scripts/generate_database.sh index 892682ef..ccd59161 100644 --- a/Docker/scripts/generate_database.sh +++ b/Docker/scripts/generate_database.sh @@ -7,7 +7,6 @@ if [ "$DOCKER_ENV" != "true" ]; then fi if [[ "$DATABASE_PROVIDER" == "postgresql" || "$DATABASE_PROVIDER" == "mysql" ]]; then - export DATABASE_URL echo "Generating database for $DATABASE_PROVIDER" echo "Database URL: $DATABASE_URL" npm run db:generate diff --git a/Dockerfile b/Dockerfile index 4c99317d..da1a66d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM node:20-alpine AS builder RUN apk update && \ - apk add git ffmpeg wget curl bash + apk add git ffmpeg wget curl bash openssl LABEL version="2.2.0" description="Api to control whatsapp features through http requests." LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" @@ -23,6 +23,7 @@ COPY ./tsup.config.ts ./ COPY ./Docker ./Docker + RUN chmod +x ./Docker/scripts/* && dos2unix ./Docker/scripts/* RUN ./Docker/scripts/generate_database.sh @@ -32,7 +33,7 @@ RUN npm run build FROM node:20-alpine AS final RUN apk update && \ - apk add tzdata ffmpeg bash + apk add tzdata ffmpeg bash openssl ENV TZ=America/Sao_Paulo diff --git a/docker-compose.yaml b/docker-compose.yaml index b286919c..ff2e7213 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,6 +2,7 @@ services: api: container_name: evolution_api image: atendai/evolution-api:homolog + build: . restart: always depends_on: - redis @@ -27,19 +28,19 @@ services: volumes: - evolution_redis:/data ports: - - 6379:6379 + - 6380:6379 postgres: container_name: postgres image: postgres:15 networks: - evolution-net - command: ["postgres", "-c", "max_connections=1000"] + command: [ "postgres", "-c", "max_connections=1000" ] restart: always ports: - - 5432:5432 + - 5433:5432 environment: - - POSTGRES_PASSWORD=PASSWORD + - POSTGRES_PASSWORD=kajsdnfjknsadkfjnkasd154asd volumes: - postgres_data:/var/lib/postgresql/data expose: diff --git a/package.json b/package.json index f7e77194..cbd5daf3 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@sentry/node": "^8.28.0", "amqplib": "^0.10.3", "axios": "^1.6.5", - "baileys": "github:EvolutionAPI/Baileys", + "baileys": "^6.7.9", "class-validator": "^0.14.1", "compression": "^1.7.4", "cors": "^2.8.5", diff --git a/prisma/postgresql-schema.prisma b/prisma/postgresql-schema.prisma index 011de9a0..74cc0f46 100644 --- a/prisma/postgresql-schema.prisma +++ b/prisma/postgresql-schema.prisma @@ -125,6 +125,7 @@ model Chat { Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) instanceId String unreadMessages Int @default(0) + @@index([instanceId]) @@index([remoteJid]) } @@ -168,6 +169,7 @@ model Message { sessionId String? session IntegrationSession? @relation(fields: [sessionId], references: [id]) + @@index([instanceId]) } @@ -183,6 +185,7 @@ model MessageUpdate { messageId String Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) instanceId String + @@index([instanceId]) @@index([messageId]) } @@ -199,6 +202,7 @@ model Webhook { updatedAt DateTime @updatedAt @db.Timestamp Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) instanceId String @unique + @@index([instanceId]) } @@ -268,6 +272,7 @@ model Setting { updatedAt DateTime @updatedAt @db.Timestamp Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) instanceId String @unique + @@index([instanceId]) } diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 0afd5318..d91b6fc1 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1855,8 +1855,8 @@ export class BaileysStartupService extends ChannelStartupService { const jid = this.createJid(number); try { - const call = await this.client.offerCall(jid, isVideo); - setTimeout(() => this.client.terminateCall(call.id, call.to), callDuration * 1000); + const call = await this.client['offerCall'](jid, isVideo); + setTimeout(() => this.client['terminateCall'](call.id, call.to), callDuration * 1000); return call; } catch (error) { diff --git a/src/main.ts b/src/main.ts index 5204b698..9419fc3b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,12 +13,35 @@ import compression from 'compression'; import cors from 'cors'; import express, { json, NextFunction, Request, Response, urlencoded } from 'express'; import { join } from 'path'; +import { Client } from 'pg'; function initWA() { waMonitor.loadInstance(); } async function bootstrap() { + const client = new Client({ + connectionString: process.env.CHATWOOT_IMPORT_DATABASE_CONNECTION_URI, + }); + + console.log(`Connecting to the database ${process.env.CHATWOOT_IMPORT_DATABASE_CONNECTION_URI}...`); + + await client + .connect() + .then(() => { + console.log('Connected to the database successfully'); + return client.query('SELECT 1'); // Simple query to check the connection + }) + .then(() => { + console.log('Ping successful'); + }) + .catch((err) => { + console.error('Error connecting to the database:', err.stack); + }) + .finally(() => { + client.end(); // Close the connection + }); + const logger = new Logger('SERVER'); const app = express(); const dsn = process.env.SENTRY_DSN;