Skip to content

Commit 873fcfa

Browse files
committed
Improve logging and add scaffolding
1 parent 2a5b5ef commit 873fcfa

39 files changed

+745
-101
lines changed

.env.example

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# WhatsApp Integration
2+
WHATSAPP_SESSION_PATH=./sessions/
3+
WHATSAPP_CHROMIUM_ARGS=--no-sandbox --disable-setuid-sandbox
4+
5+
# MongoDB
6+
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/dbname
7+
8+
# JWT
9+
JWT_SECRET=your_jwt_secret
10+
JWT_EXPIRES_IN=1d
11+
12+
# Email Verification
13+
SMTP_HOST=smtp.yourhost.com
14+
SMTP_PORT=465
15+
SMTP_USER=your@email.com
16+
SMTP_PASS=your_password
17+
18+
# OpenAI (optional)
19+
OPENAI_API_KEY=sk-...
20+
21+
# App URLs
22+
BASE_URL=http://localhost:5000
23+
FRONTEND_URL=http://localhost:3000
24+
25+
# Enable test mode to avoid real WhatsApp messages
26+
TEST_MODE=true

.env.production

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Production environment variables
2+
MONGODB_URI=
3+
JWT_SECRET=
4+
JWT_EXPIRES_IN=1d
5+
SMTP_HOST=
6+
SMTP_PORT=465
7+
SMTP_USER=
8+
SMTP_PASS=
9+
BASE_URL=https://pingu.ro
10+
FRONTEND_URL=https://app.pingu.ro

.github/workflows/ci.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: CI
2+
on:
3+
push:
4+
branches: [ main ]
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v3
10+
- uses: actions/setup-node@v3
11+
with:
12+
node-version: 18
13+
- run: npm install --prefix backend
14+
- run: node scripts/db-check.js
15+
env:
16+
MONGODB_URI: ${{ secrets.MONGODB_URI }}

.gitignore

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
node_modules/
1+
node_modules
2+
sessions
23
.env
3-
.DS_Store
4-
*.log
4+
frontend/node_modules
5+
backend/node_modules
6+
7+
# managed by open-wa
8+
**.data.json
9+
**.node-persist**
10+
**_IGNORE_**
11+
# end managed by open-wa

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM node:18
2+
WORKDIR /app
3+
COPY backend ./backend
4+
RUN npm install --prefix backend
5+
CMD ["node", "backend/app.js"]

README.md

Lines changed: 84 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,124 @@
1+
# PingU.RO
12

2-
# 🤖 WhatsApp Chat GPT Bot (OpenAI + @open-wa/wa-automate)
3+
PingU.RO is a WhatsApp powered CRM web application that lets you manage contacts, appointments and marketing campaigns. It uses **wa-automate-nodejs** for WhatsApp messaging and **MongoDB** for persistence.
34

4-
This is a lightweight WhatsApp bot that responds to private messages with financial market predictions using OpenAI's GPT-3.5 model. It uses [@open-wa/wa-automate](https://github.com/open-wa/wa-automate-nodejs) to interface with WhatsApp Web.
5+
## Features
56

6-
---
7+
- User registration with email verification
8+
- JWT based login/logout
9+
- Connect your WhatsApp account and keep the session persistent
10+
- Manage contacts individually or via CSV import
11+
- Schedule appointments with WhatsApp reminders
12+
- Create marketing campaigns sent via WhatsApp
13+
- Optional test mode to avoid sending real messages
714

8-
## ⚙️ Features
15+
## Project Structure
916

10-
- Auto-responds to incoming private WhatsApp messages
11-
- Uses ChatGPT to provide financial insights and predictions
12-
- Short, confident responses in expert tone
13-
14-
---
17+
```
18+
/backend - Express API
19+
/frontend - React app
20+
/shared - shared interfaces
21+
/test - seed scripts
22+
```
1523

16-
## 📦 Setup Instructions
24+
## Installation
1725

18-
### 1. Clone or download this repo
26+
### Backend
1927

2028
```bash
21-
git clone https://github.com/dumebai/whatsapp-chat-gpt.git
22-
cd whatsapp-chat-gpt
29+
cd backend
30+
npm install
2331
```
2432

25-
### 2. Install required packages
33+
### Frontend
2634

2735
```bash
36+
cd ../frontend
2837
npm install
2938
```
3039

31-
### 3. Create a `.env` file
40+
## Environment Variables
3241

33-
Inside the root directory, create a `.env` file:
42+
Create `.env` in the project root or inside backend with the following keys (see `.env.example`):
3443

3544
```env
36-
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
45+
WHATSAPP_SESSION_PATH=./sessions/
46+
WHATSAPP_CHROMIUM_ARGS=--no-sandbox --disable-setuid-sandbox
47+
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/dbname
48+
JWT_SECRET=supersecret
49+
JWT_EXPIRES_IN=1d
50+
SMTP_HOST=smtp.example.com
51+
SMTP_PORT=465
52+
SMTP_USER=user@example.com
53+
SMTP_PASS=password
54+
OPENAI_API_KEY=sk-...
55+
BASE_URL=http://localhost:5000
56+
FRONTEND_URL=http://localhost:3000
57+
TEST_MODE=true
3758
```
3859

39-
You can get your API key from [https://platform.openai.com/account/api-keys](https://platform.openai.com/account/api-keys)
60+
For the React frontend copy `frontend/.env.example` to `frontend/.env` and adjust the API base URL if needed.
61+
62+
## Running in Development
4063

41-
---
64+
Start the backend API:
4265

43-
## 🚀 Run the Bot
66+
```bash
67+
cd backend
68+
npm run dev
69+
```
70+
71+
In a new terminal start the frontend:
4472

4573
```bash
74+
cd frontend
4675
npm start
4776
```
4877

49-
A QR code will appear in the terminal – scan it using your WhatsApp mobile app to activate the session.
78+
Scan the QR displayed in the backend terminal to connect WhatsApp.
5079

51-
---
80+
## Seed Data
5281

53-
## 💬 Example Prompts
82+
```
83+
npm run seed
84+
```
5485

55-
- “What's your take on Bitcoin this week?”
56-
- “Is it a good time to invest in Tesla?”
57-
- “What's the market sentiment for Ethereum?”
58-
- “Will inflation go down in the next quarter?”
86+
This creates a user `test@pingu.ro` with password `password`.
5987

60-
---
88+
## Usage
6189

62-
## 🧠 Tech Stack
90+
1. Register a new account and verify your email
91+
2. Login to access the dashboard
92+
3. Connect your WhatsApp account via QR code
93+
4. Add contacts and schedule appointments or campaigns
94+
5. Reminders and campaign messages are sent automatically
6395

64-
- `@open-wa/wa-automate` – for WhatsApp automation
65-
- `openai` – to communicate with GPT-3.5
66-
- `dotenv` – to securely load environment variables
96+
## Production Notes
97+
98+
To build for production run:
99+
```bash
100+
cd frontend
101+
npm run build
102+
```
103+
Copy the `build` folder to your preferred static host or configure your Node.js server to serve it. The backend can be started with:
104+
```bash
105+
node backend/app.js
106+
```
67107

68-
---
69108

70-
## 🔒 Notes
109+
- The frontend can be deployed to **Vercel**.
110+
- Any Node.js hosting supporting MongoDB can run the backend.
111+
112+
### Docker
113+
114+
```bash
115+
docker-compose up --build
116+
```
71117

72-
- This bot only responds to **private messages**, not group chats.
73-
- Make sure to **keep your OpenAI key private** and monitor usage limits to avoid unexpected charges.
118+
### CI
74119

75-
---
120+
GitHub Actions validates the backend on each push by installing dependencies and checking the database connection.
76121

77-
## 📜 License
122+
## License
78123

79-
MIT – free to use, modify, and commercialize with attribution. If you find this useful, give credit or build something big with it 🚀
124+
MIT

backend/app.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const express = require('express');
2+
const mongoose = require('mongoose');
3+
const cors = require('cors');
4+
require('dotenv').config();
5+
6+
const mongoUri = (process.env.MONGODB_URI || '').trim();
7+
console.log('Connecting to MongoDB at', mongoUri);
8+
9+
const authRoutes = require('./routes/authRoutes');
10+
const contactRoutes = require('./routes/contactRoutes');
11+
const appointmentRoutes = require('./routes/appointmentRoutes');
12+
const campaignRoutes = require('./routes/campaignRoutes');
13+
14+
const app = express();
15+
app.use(cors());
16+
app.use(express.json());
17+
18+
process.on('unhandledRejection', err => {
19+
console.error('Unhandled rejection:', err);
20+
});
21+
22+
mongoose.connection.on('connected', () => console.log('MongoDB connected'));
23+
mongoose.connection.on('error', err => {
24+
console.error('MongoDB connection error:', err);
25+
});
26+
mongoose.connection.on('disconnected', () => console.log('MongoDB disconnected'));
27+
28+
mongoose.connect(mongoUri, {
29+
useNewUrlParser: true,
30+
useUnifiedTopology: true
31+
}).catch(err => console.error('Initial connection error:', err));
32+
33+
app.use('/api/auth', authRoutes);
34+
app.use('/api/contacts', contactRoutes);
35+
app.use('/api/appointments', appointmentRoutes);
36+
app.use('/api/campaigns', campaignRoutes);
37+
38+
const PORT = process.env.PORT || 5000;
39+
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const Appointment = require('../models/Appointment');
2+
const Contact = require('../models/Contact');
3+
const { scheduleReminder } = require('../services/reminderService');
4+
const { sendError } = require('../utils/responses');
5+
6+
/**
7+
* @notice Create an appointment and schedule reminder
8+
* @param {Object} req Express request
9+
* @param {Object} res Express response
10+
* @return {Promise<void>}
11+
*/
12+
exports.createAppointment = async (req, res) => {
13+
const { title, contacts, description, start, reminderMinutes } = req.body;
14+
if (!title || !start) return sendError(res, 400, 'Title and start time required');
15+
const appointment = await Appointment.create({
16+
user: req.userId,
17+
title,
18+
contacts,
19+
description,
20+
start,
21+
reminderMinutes
22+
});
23+
scheduleReminder(appointment);
24+
res.json(appointment);
25+
};
26+
27+
/**
28+
* @notice List appointments for the user
29+
* @param {Object} req Express request
30+
* @param {Object} res Express response
31+
* @return {Promise<void>}
32+
*/
33+
exports.listAppointments = async (req, res) => {
34+
const appointments = await Appointment.find({ user: req.userId }).populate('contacts');
35+
res.json(appointments);
36+
};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const bcrypt = require('bcryptjs');
2+
const crypto = require('crypto');
3+
const User = require('../models/User');
4+
const { generateToken } = require('../utils/token');
5+
const { sendVerificationEmail } = require('../utils/email');
6+
const { sendError } = require('../utils/responses');
7+
8+
/**
9+
* @notice Register a new user and send verification email
10+
* @param {Object} req Express request
11+
* @param {Object} res Express response
12+
* @return {Promise<void>}
13+
*/
14+
exports.register = async (req, res) => {
15+
const { email, password } = req.body;
16+
const existing = await User.findOne({ email });
17+
if (existing) return sendError(res, 400, 'User exists');
18+
const hash = await bcrypt.hash(password, 10);
19+
const verificationToken = crypto.randomBytes(20).toString('hex');
20+
const user = await User.create({ email, password: hash, verificationToken });
21+
if (process.env.TEST_MODE !== 'true')
22+
await sendVerificationEmail(email, verificationToken);
23+
res.json({ message: 'Registered, check email to verify' });
24+
};
25+
26+
/**
27+
* @notice Verify a user's email
28+
* @param {Object} req Express request
29+
* @param {Object} res Express response
30+
* @return {Promise<void>}
31+
*/
32+
exports.verify = async (req, res) => {
33+
const { token } = req.query;
34+
const user = await User.findOne({ verificationToken: token });
35+
if (!user) return sendError(res, 400, 'Invalid token');
36+
user.verified = true;
37+
user.verificationToken = undefined;
38+
await user.save();
39+
res.json({ message: 'Verified' });
40+
};
41+
42+
/**
43+
* @notice Authenticate user and return JWT token
44+
* @param {Object} req Express request
45+
* @param {Object} res Express response
46+
* @return {Promise<void>}
47+
*/
48+
exports.login = async (req, res) => {
49+
const { email, password } = req.body;
50+
const user = await User.findOne({ email });
51+
if (!user) return sendError(res, 400, 'Invalid credentials');
52+
const match = await bcrypt.compare(password, user.password);
53+
if (!match) return sendError(res, 400, 'Invalid credentials');
54+
if (!user.verified) return sendError(res, 403, 'Verify email');
55+
const token = generateToken({ id: user._id });
56+
res.json({ token });
57+
};

0 commit comments

Comments
 (0)