base on A very simple Ticket System based on WhatsApp messages, that allow multi-users in same WhatsApp account. [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=VWW3BHW4AWHUY&item_name=Desenvolvimento+de+Software¤cy_code=BRL)
[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B21084%2Fgithub.com%2Fcanove%2Fwhaticket.svg?type=shield)](https://app.fossa.com/projects/custom%2B21084%2Fgithub.com%2Fcanove%2Fwhaticket?ref=badge_shield)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=canove_whaticket&metric=alert_status)](https://sonarcloud.io/dashboard?id=canove_whaticket)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=canove_whaticket&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=canove_whaticket)
[![Discord Chat](https://img.shields.io/discord/784109818247774249.svg?logo=discord)](https://discord.gg/Dp2tTZRYHg)
[![Forum](https://img.shields.io/badge/forum-online-blue.svg?logo=discourse)](https://whaticket.online/)
# WhaTicket!
**NOTE**: The new version of whatsapp-web.js required Node 14. Upgrade your installations to keep using it.
A _very simple_ Ticket System based on WhatsApp messages.
Backend uses [whatsapp-web.js](https://github.com/pedroslopez/whatsapp-web.js) to receive and send WhatsApp messages, create tickets from them and store all in a MySQL database.
Frontend is a full-featured multi-user _chat app_ bootstrapped with react-create-app and Material UI, that comunicates with backend using REST API and Websockets. It allows you to interact with contacts, tickets, send and receive WhatsApp messages.
**NOTE**: I can't guarantee you will not be blocked by using this method, although it has worked for me. WhatsApp does not allow bots or unofficial clients on their platform, so this shouldn't be considered totally safe.
## How it works?
On every new message received in an associated WhatsApp, a new Ticket is created. Then, this ticket can be reached in a _queue_ on _Tickets_ page, where you can assign ticket to your yourself by _aceppting_ it, respond ticket message and eventually _resolve_ it.
Subsequent messages from same contact will be related to first **open/pending** ticket found.
If a contact sent a new message in less than 2 hours interval, and there is no ticket from this contact with **pending/open** status, the newest **closed** ticket will be reopen, instead of creating a new one.
## Screenshots
![](https://github.com/canove/whaticket/raw/master/images/whaticket-queues.gif)
<img src="https://raw.githubusercontent.com/canove/whaticket/master/images/chat2.png" width="350"> <img src="https://raw.githubusercontent.com/canove/whaticket/master/images/chat3.png" width="350"> <img src="https://raw.githubusercontent.com/canove/whaticket/master/images/multiple-whatsapps2.png" width="350"> <img src="https://raw.githubusercontent.com/canove/whaticket/master/images/contacts1.png" width="350">
## Features
- Have multiple users chating in same WhatsApp Number β
- Connect to multiple WhatsApp accounts and receive all messages in one place β
π
- Create and chat with new contacts without touching cellphone β
- Send and receive message β
- Send media (images/audio/documents) β
- Receive media (images/audio/video/documents) β
## Installation and Usage (Linux Ubuntu - Development)
Create Mysql Database using docker:
_Note_: change MYSQL_DATABASE, MYSQL_PASSWORD, MYSQL_USER and MYSQL_ROOT_PASSWORD.
```bash
docker run --name whaticketdb -e MYSQL_ROOT_PASSWORD=strongpassword -e MYSQL_DATABASE=whaticket -e MYSQL_USER=whaticket -e MYSQL_PASSWORD=whaticket --restart always -p 3306:3306 -d mariadb:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_bin
# Or run using `docker-compose` as below
# Before copy .env.example to .env first and set the variables in the file.
docker-compose up -d mysql
# To administer this mysql database easily using phpmyadmin.
# It will run by default on port 9000, but can be changed in .env using `PMA_PORT`
docker-compose -f docker-compose.phpmyadmin.yaml up -d
```
Install puppeteer dependencies:
```bash
sudo apt-get install -y libxshmfence-dev libgbm-dev wget unzip fontconfig locales gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils
```
Clone this repo
```bash
git clone https://github.com/canove/whaticket/ whaticket
```
Go to backend folder and create .env file:
```bash
cp .env.example .env
nano .env
```
Fill `.env` file with environment variables:
```bash
NODE_ENV=DEVELOPMENT #it helps on debugging
BACKEND_URL=http://localhost
FRONTEND_URL=https://localhost:3000
PROXY_PORT=8080
PORT=8080
DB_HOST= #DB host IP, usually localhost
DB_DIALECT=
DB_USER=
DB_PASS=
DB_NAME=
JWT_SECRET=3123123213123
JWT_REFRESH_SECRET=75756756756
```
Install backend dependencies, build app, run migrations and seeds:
```bash
npm install
npm run build
npx sequelize db:migrate
npx sequelize db:seed:all
```
Start backend:
```bash
npm start
```
Open a second terminal, go to frontend folder and create .env file:
```bash
nano .env
REACT_APP_BACKEND_URL = http://localhost:8080/ # Your previous configured backend app URL.
```
Start frontend app:
```bash
npm start
```
- Go to http://your_server_ip:3000/signup
- Create an user and login with it.
- On the sidebard, go to _Connections_ page and create your first WhatsApp connection.
- Wait for QR CODE button to appear, click it and read qr code.
- Done. Every message received by your synced WhatsApp number will appear in Tickets List.
## Basic production deployment
### Using Ubuntu 20.04 VPS
All instructions below assumes you are NOT running as root, since it will give an error in puppeteer. So let's start creating a new user and granting sudo privileges to it:
```bash
adduser deploy
usermod -aG sudo deploy
```
Now we can login with this new user:
```bash
su deploy
```
You'll need two subdomains forwarding to yours VPS ip to follow these instructions. We'll use `myapp.mydomain.com` to frontend and `api.mydomain.com` to backend in the following example.
Update all system packages:
```bash
sudo apt update && sudo apt upgrade
```
Install node, and confirm node command is available:
```bash
curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v
npm -v
```
Install docker and add you user to docker group:
```bash
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 usermod -aG docker ${USER}
su - ${USER}
```
Create Mysql Database using docker:
_Note_: change MYSQL_DATABASE, MYSQL_PASSWORD, MYSQL_USER and MYSQL_ROOT_PASSWORD.
```bash
docker run --name whaticketdb -e MYSQL_ROOT_PASSWORD=strongpassword -e MYSQL_DATABASE=whaticket -e MYSQL_USER=whaticket -e MYSQL_PASSWORD=whaticket --restart always -p 3306:3306 -d mariadb:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_bin
# Or run using `docker-compose` as below
# Before copy .env.example to .env first and set the variables in the file.
docker-compose up -d mysql
# To administer this mysql database easily using phpmyadmin.
# It will run by default on port 9000, but can be changed in .env using `PMA_PORT`
docker-compose -f docker-compose.phpmyadmin.yaml up -d
```
Clone this repository:
```bash
cd ~
git clone https://github.com/canove/whaticket whaticket
```
Create backend .env file and fill with details:
```bash
cp whaticket/backend/.env.example whaticket/backend/.env
nano whaticket/backend/.env
```
```bash
NODE_ENV=
BACKEND_URL=https://api.mydomain.com #USE HTTPS HERE, WE WILL ADD SSL LATTER
FRONTEND_URL=https://myapp.mydomain.com #USE HTTPS HERE, WE WILL ADD SSL LATTER, CORS RELATED!
PROXY_PORT=443 #USE NGINX REVERSE PROXY PORT HERE, WE WILL CONFIGURE IT LATTER
PORT=8080
DB_HOST=localhost
DB_DIALECT=
DB_USER=
DB_PASS=
DB_NAME=
JWT_SECRET=3123123213123
JWT_REFRESH_SECRET=75756756756
```
Install puppeteer dependencies:
```bash
sudo apt-get install -y libxshmfence-dev libgbm-dev wget unzip fontconfig locales gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils
```
Install backend dependencies, build app, run migrations and seeds:
```bash
cd whaticket/backend
npm install
npm run build
npx sequelize db:migrate
npx sequelize db:seed:all
```
Start it with `npm start`, you should see: `Server started on port...` on console. Hit `CTRL + C` to exit.
Install pm2 **with sudo**, and start backend with it:
```bash
sudo npm install -g pm2
pm2 start dist/server.js --name whaticket-backend
```
Make pm2 auto start after reboot:
```bash
pm2 startup ubuntu -u `YOUR_USERNAME`
```
Copy the last line outputed from previus command and run it, its something like:
```bash
sudo env PATH=\$PATH:/usr/bin pm2 startup ubuntu -u YOUR_USERNAME --hp /home/YOUR_USERNAM
```
Go to frontend folder and install dependencies:
```bash
cd ../frontend
npm install
```
Create frontend .env file and fill it ONLY with your backend address, it should look like this:
```bash
REACT_APP_BACKEND_URL = https://api.mydomain.com/
```
Build frontend app:
```bash
npm run build
```
Start frontend with pm2, and save pm2 process list to start automatically after reboot:
```bash
pm2 start server.js --name whaticket-frontend
pm2 save
```
To check if it's running, run `pm2 list`, it should look like:
```bash
deploy@ubuntu-whats:~$ pm2 list
βββββββ¬ββββββββββββββββββββββββββ¬ββββββββββββββ¬ββββββββββ¬ββββββββββ¬βββββββββββ¬βββββββββ¬βββββββ¬ββββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ
β id β name β namespace β version β mode β pid β uptime β . β status β cpu β mem β user β watching β
βββββββΌββββββββββββββββββββββββββΌββββββββββββββΌββββββββββΌββββββββββΌβββββββββββΌβββββββββΌβββββββΌββββββββββββΌβββββββββββΌβββββββββββΌβββββββββββΌβββββββββββ€
β 1 β whaticket-frontend β default β 0.1.0 β fork β 179249 β 12D β 0 β online β 0.3% β 50.2mb β deploy β disabled β
β 6 β whaticket-backend β default β 1.0.0 β fork β 179253 β 12D β 15 β online β 0.3% β 118.5mb β deploy β disabled β
βββββββ΄ββββββββββββββββββββββββββ΄ββββββββββββββ΄ββββββββββ΄ββββββββββ΄βββββββββββ΄βββββββββ΄βββββββ΄ββββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ
```
Install nginx:
```bash
sudo apt install nginx
```
Remove nginx default site:
```bash
sudo rm /etc/nginx/sites-enabled/default
```
Create a new nginx site to frontend app:
```bash
sudo nano /etc/nginx/sites-available/whaticket-frontend
```
Edit and fill it with this information, changing `server_name` to yours equivalent to `myapp.mydomain.com`:
```bash
server {
server_name myapp.mydomain.com;
location / {
proxy_pass http://127.0.0.1:3333;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
}
```
Create another one to backend api, changing `server_name` to yours equivalent to `api.mydomain.com`, and `proxy_pass` to your localhost backend node server URL:
```bash
sudo cp /etc/nginx/sites-available/whaticket-frontend /etc/nginx/sites-available/whaticket-backend
sudo nano /etc/nginx/sites-available/whaticket-backend
```
```bash
server {
server_name api.mydomain.com;
location / {
proxy_pass http://127.0.0.1:8080;
......
}
```
Create a symbolic links to enable nginx sites:
```bash
sudo ln -s /etc/nginx/sites-available/whaticket-frontend /etc/nginx/sites-enabled
sudo ln -s /etc/nginx/sites-available/whaticket-backend /etc/nginx/sites-enabled
```
By default, nginx limit body size to 1MB, which isn't enough for some media uploads. Lets change it to 20MB, adding a new line to config file:
```bash
sudo nano /etc/nginx/nginx.conf
...
http {
...
client_max_body_size 20M; # HANDLE BIGGER UPLOADS
}
```
Test nginx configuration and restart server:
```bash
sudo nginx -t
sudo service nginx restart
```
Now, enable SSL (https) on your sites to use all app features like notifications and sending audio messages. An easy way to this is using Certbot:
Install certbot:
```bash
sudo snap install --classic certbot
sudo apt update
```
Enable SSL on nginx (Fill / Accept all information required):
```bash
sudo certbot --nginx
```
### Using docker and docker-compose
To run WhaTicket using docker you must perform the following steps:
```bash
cp .env.example .env
```
Now it will be necessary to configure the .env using its information, the variables are the same as those mentioned in the deployment using ubuntu, with the exception of mysql settings that were not in the .env.
```bash
# MYSQL
MYSQL_ENGINE= # default: mariadb
MYSQL_VERSION= # default: 10.6
MYSQL_ROOT_PASSWORD=strongpassword # change it please
MYSQL_DATABASE=whaticket
MYSQL_PORT=3306 # default: 3306; Use this port to expose mysql server
TZ=America/Fortaleza # default: America/Fortaleza; Timezone for mysql
# BACKEND
BACKEND_PORT= # default: 8080; but access by host not use this port
BACKEND_SERVER_NAME=api.mydomain.com
BACKEND_URL=https://api.mydomain.com
PROXY_PORT=443
JWT_SECRET=3123123213123 # change it please
JWT_REFRESH_SECRET=75756756756 # change it please
# FRONTEND
FRONTEND_PORT=80 # default: 3000; Use port 80 to expose in production
FRONTEND_SSL_PORT=443 # default: 3001; Use port 443 to expose in production
FRONTEND_SERVER_NAME=myapp.mydomain.com
FRONTEND_URL=https://myapp.mydomain.com
# BROWSERLESS
MAX_CONCURRENT_SESSIONS= # default: 1; Use only if using browserless
```
After defining the variables, run the following command:
```bash
docker-compose up -d --build
```
On the `first` run it will be necessary to seed the database tables using the following command:
```bash
docker-compose exec backend npx sequelize db:seed:all
```
#### SSL Certificate
To deploy the ssl certificate, add it to the `ssl/certs` folder. Inside it there should be a `backend` and a `frontend` folder, and each of them should contain the files `fullchain.pem` and `privkey.pem`, as in the structure below:
```bash
.
βββ certs
βΒ Β βββ backend
βΒ Β βΒ Β βββ fullchain.pem
βΒ Β βΒ Β βββ privkey.pem
βΒ Β βββ frontend
βΒ Β βββ fullchain.pem
βΒ Β βββ privkey.pem
βββ www
```
To generate the certificate files use `certbot` which can be installed using snap, I used the following command:
Note: The frontend container that runs nginx is already prepared to receive the request made by certboot to validate the certificate.
```bash
# BACKEND
certbot certonly --cert-name backend --webroot --webroot-path ./ssl/www/ -d api.mydomain.com
# FRONTEND
certbot certonly --cert-name frontend --webroot --webroot-path ./ssl/www/ -d myapp.mydomain.com
```
## Access Data
User:
[email protected]
Password: admin
## Upgrading
WhaTicket is a working in progress and we are adding new features frequently. To update your old installation and get all the new features, you can use a bash script like this:
**Note**: Always check the .env.example and adjust your .env file before upgrading, since some new variable may be added.
```bash
nano updateWhaticket
```
```bash
#!/bin/bash
echo "Updating Whaticket, please wait."
cd ~
cd whaticket
git pull
cd backend
npm install
rm -rf dist
npm run build
npx sequelize db:migrate
npx sequelize db:seed
cd ../frontend
npm install
rm -rf build
npm run build
pm2 restart all
echo "Update finished. Enjoy!"
```
Make it executable and run it:
```bash
chmod +x updateWhaticket
./updateWhaticket
```
## Contributing
This project helps you and you want to help keep it going? Buy me a coffee:
<a href="https://www.buymeacoffee.com/canove" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 61px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
Para doaçáes em BRL, utilize o Paypal:
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=VWW3BHW4AWHUY&item_name=Desenvolvimento+de+Software¤cy_code=BRL)
Any help and suggestions will be apreciated.
## Disclaimer
I just started leaning Javascript a few months ago and this is my first project. It may have security issues and many bugs. I recommend using it only on local network.
This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with WhatsApp or any of its subsidiaries or its affiliates. The official WhatsApp website can be found at https://whatsapp.com. "WhatsApp" as well as related names, marks, emblems and images are registered trademarks of their respective owners.
", Assign "at most 3 tags" to the expected json: {"id":"7639","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"