commit b8bd6b4d89793ac3ea46dfdd9c30c58fef4b536d
parent 38fb84fddd343ebfc12d2849763651e6d58614ed
Author: AMIT DUTTA <amitdutta4255@gmail.com>
Date: Mon, 16 Feb 2026 20:12:33 +0530
Add files via upload
Diffstat:
6 files changed, 435 insertions(+), 0 deletions(-)
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
@@ -0,0 +1,19 @@
+# Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the overall community
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project maintainer at mail@amit.is-a.dev. All complaints will be reviewed and investigated promptly and fairly.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
@@ -0,0 +1,19 @@
+# Contributing to WhatsApp Logger
+
+Thank you for your interest in contributing.
+
+## How to Contribute
+
+1. Fork the repository.
+2. Create a new branch for your feature or fix.
+3. Commit your changes.
+4. Push to your fork and submit a Pull Request.
+
+## Reporting Bugs
+
+If you encounter an issue that is not a security vulnerability, please open an Issue on GitHub or email mail@amit.is-a.dev.
+
+## Guidelines
+
+- **No Logging of Data:** Do not submit code that adds `console.log` or print statements revealing message content, phone numbers, or user data. Pull Requests containing such logs will be rejected to protect user privacy.
+- **Clean Code:** Ensure your code is readable and commented where necessary.
diff --git a/Dockerfile b/Dockerfile
@@ -0,0 +1,30 @@
+FROM node:20-slim
+
+# Install system dependencies
+# We add git because some npm packages require it to fetch dependencies.
+# We add python3, make, and g++ because some crypto libraries used by Baileys
+# might need to compile native code.
+RUN apt-get update && apt-get install -y \
+ git \
+ python3 \
+ make \
+ g++ \
+ && rm -rf /var/lib/apt/lists/*
+
+# Create app directory
+WORKDIR /usr/src/app
+
+# Install dependencies
+# A wildcard is used to ensure both package.json AND package-lock.json are copied
+COPY package*.json ./
+
+RUN npm install
+
+# Bundle app source
+COPY . .
+
+# Expose the port the app runs on
+EXPOSE 3000
+
+# Start command
+CMD [ "node", "index.js" ]
diff --git a/SECURITY.md b/SECURITY.md
@@ -0,0 +1,25 @@
+# Security Policy
+
+## Supported Versions
+
+Please use the latest version of this project to ensure you have the latest security patches.
+
+## Reporting a Vulnerability
+
+We take the security of this project seriously. If you discover a security vulnerability, please do **NOT** open a public issue.
+
+Instead, please send an email to:
+**amitddutta4255@gmail.com**
+
+Please include:
+- A description of the vulnerability.
+- Steps to reproduce the issue.
+- Any relevant code snippets or logs (redacted).
+
+We will acknowledge your email within 48 hours and will keep you updated on the progress of the fix.
+
+## Privacy & Logs
+
+This application handles private communication data.
+- **Strict No-Logging Policy:** The application is designed to never output message content to system logs (stdout/stderr).
+- If you modify the source code, you are responsible for maintaining this policy to prevent leaking private messages into server logs.
diff --git a/index.js b/index.js
@@ -0,0 +1,320 @@
+const {
+ default: makeWASocket,
+ useMultiFileAuthState,
+ DisconnectReason,
+ fetchLatestBaileysVersion
+} = require('@whiskeysockets/baileys');
+const express = require('express');
+const QRCode = require('qrcode');
+const pino = require('pino');
+const admin = require('firebase-admin');
+const fs = require('fs');
+const path = require('path');
+const crypto = require('crypto');
+
+// --- CONFIGURATION ---
+const PORT = process.env.PORT || 3000;
+const AUTH_USER = process.env.AUTH_USER;
+const AUTH_PASS = process.env.AUTH_PASS;
+
+// Initialize Express
+const app = express();
+app.use(express.urlencoded({ extended: true })); // Parse form data
+app.use(express.json()); // Parse JSON bodies (for API requests)
+
+// --- FIREBASE SETUP ---
+let serviceAccount;
+try {
+ if (process.env.FIREBASE_SERVICE_ACCOUNT) {
+ serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
+ } else {
+ serviceAccount = require('./serviceAccountKey.json');
+ }
+
+ admin.initializeApp({
+ credential: admin.credential.cert(serviceAccount)
+ });
+ console.log("System: Firebase Admin initialized successfully.");
+} catch (error) {
+ console.error("System Error: Failed to initialize Firebase. Make sure FIREBASE_SERVICE_ACCOUNT env var is set.");
+ process.exit(1);
+}
+
+const db = admin.firestore();
+
+// --- BAILEYS SETUP ---
+let qrCodeData = null;
+let sock = null;
+let isConnected = false;
+
+async function startWhatsApp() {
+ const logger = pino({ level: 'silent' });
+
+ const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');
+ const { version } = await fetchLatestBaileysVersion();
+
+ sock = makeWASocket({
+ version,
+ logger,
+ printQRInTerminal: false,
+ auth: state,
+ browser: ["WhatsApp Logger by notamitgamer", "Chrome", "1.0.0"],
+ syncFullHistory: false
+ });
+
+ sock.ev.on('connection.update', (update) => {
+ const { connection, lastDisconnect, qr } = update;
+
+ if (qr) {
+ qrCodeData = qr;
+ isConnected = false;
+ }
+
+ if (connection === 'close') {
+ isConnected = false;
+ const shouldReconnect = (lastDisconnect.error)?.output?.statusCode !== DisconnectReason.loggedOut;
+ if (shouldReconnect) {
+ startWhatsApp();
+ }
+ } else if (connection === 'open') {
+ console.log("System: Connection Open and Authenticated");
+ qrCodeData = null;
+ isConnected = true;
+ }
+ });
+
+ sock.ev.on('creds.update', saveCreds);
+
+ sock.ev.on('messages.upsert', async ({ messages, type }) => {
+ if (type !== 'notify' && type !== 'append') return;
+
+ for (const msg of messages) {
+ try {
+ if (!msg.message) continue;
+
+ const remoteJid = msg.key.remoteJid;
+ if (remoteJid === 'status@broadcast') continue;
+
+ const textContent =
+ msg.message.conversation ||
+ msg.message.extendedTextMessage?.text ||
+ msg.message.imageMessage?.caption ||
+ msg.message.videoMessage?.caption ||
+ "";
+
+ if (!textContent) continue;
+
+ const timestamp = msg.messageTimestamp
+ ? (typeof msg.messageTimestamp === 'number' ? msg.messageTimestamp : msg.messageTimestamp.low)
+ : Math.floor(Date.now() / 1000);
+
+ const isFromMe = msg.key.fromMe || false;
+ const senderName = isFromMe ? "Me" : (msg.pushName || "Unknown");
+ const phoneNumber = remoteJid.split('@')[0]; // Extract number from JID
+
+ // --- FIX: Create/Update Parent Chat Document ---
+ // This makes the chat visible in the list automatically
+ await db.collection('Chats').doc(remoteJid).set({
+ lastActive: timestamp,
+ displayName: senderName,
+ phoneNumber: phoneNumber, // Added: Save phone number/ID to Chat
+ id: remoteJid
+ }, { merge: true });
+
+ // --- Save Message ---
+ await db.collection('Chats')
+ .doc(remoteJid)
+ .collection('Messages')
+ .doc(msg.key.id)
+ .set({
+ text: textContent,
+ senderId: remoteJid,
+ senderName: senderName,
+ senderPhoneNumber: phoneNumber, // Added: Save phone number/ID to Message
+ timestamp: timestamp,
+ fromMe: isFromMe,
+ id: msg.key.id
+ }, { merge: true });
+
+ } catch (err) {
+ // Silent error handling
+ }
+ }
+ });
+}
+
+// --- AUTH UTILS ---
+const SESSION_SECRET = crypto.createHash('sha256').update(AUTH_PASS || 'default').digest('hex');
+
+function parseCookies(request) {
+ const list = {};
+ const rc = request.headers.cookie;
+ if (rc) {
+ rc.split(';').forEach((cookie) => {
+ const parts = cookie.split('=');
+ list[parts.shift().trim()] = decodeURI(parts.join('='));
+ });
+ }
+ return list;
+}
+
+// --- EXPRESS ROUTES ---
+
+// 1. PUBLIC ROUTE: Ping
+app.get('/ping', (req, res) => {
+ res.status(200).send('Pong');
+});
+
+// 2. PUBLIC ROUTE: Verify Credentials API
+app.post('/api/verify', (req, res) => {
+ // CORS Headers
+ res.header("Access-Control-Allow-Origin", "*");
+ res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
+
+ const { username, password } = req.body;
+
+ if (username === AUTH_USER && password === AUTH_PASS) {
+ return res.json({ success: true });
+ } else {
+ return res.status(401).json({ success: false });
+ }
+});
+
+app.options('/api/verify', (req, res) => {
+ res.header("Access-Control-Allow-Origin", "*");
+ res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
+ res.sendStatus(200);
+});
+
+// 3. LOGIN PAGE (GET)
+app.get('/login', (req, res) => {
+ res.send(`
+ <html>
+ <body style="font-family: sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f2f5;">
+ <form action="/login" method="POST" style="background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); width: 300px;">
+ <h2 style="margin-top: 0; text-align: center;">WhatsApp Logger</h2>
+ <div style="margin-bottom: 1rem;">
+ <label style="display: block; margin-bottom: 0.5rem;">Username</label>
+ <input type="text" name="username" required style="width: 100%; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;">
+ </div>
+ <div style="margin-bottom: 1rem;">
+ <label style="display: block; margin-bottom: 0.5rem;">Password</label>
+ <input type="password" name="password" required style="width: 100%; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;">
+ </div>
+ <div style="margin-bottom: 1rem;">
+ <label style="display: flex; align-items: center; font-size: 0.9rem;">
+ <input type="checkbox" name="remember" value="yes" style="margin-right: 0.5rem;">
+ Keep me logged in for 5 mins
+ </label>
+ </div>
+ <button type="submit" style="width: 100%; padding: 0.75rem; background: #25D366; color: white; border: none; border-radius: 4px; font-weight: bold; cursor: pointer;">Login</button>
+ </form>
+ </body>
+ </html>
+ `);
+});
+
+// 4. LOGIN ACTION (POST)
+app.post('/login', (req, res) => {
+ const { username, password, remember } = req.body;
+
+ if (username === AUTH_USER && password === AUTH_PASS) {
+ let cookieSettings = 'HttpOnly; Path=/;';
+
+ if (remember === 'yes') {
+ cookieSettings += ' Max-Age=300;';
+ }
+
+ res.setHeader('Set-Cookie', `auth_session=${SESSION_SECRET}; ${cookieSettings}`);
+ return res.redirect('/');
+ }
+
+ res.status(401).send('Invalid credentials. <a href="/login">Try again</a>');
+});
+
+// 5. LOGOUT ACTION
+app.get('/logout', (req, res) => {
+ res.setHeader('Set-Cookie', 'auth_session=; Max-Age=0; Path=/;');
+ res.redirect('/login');
+});
+
+// --- MIDDLEWARE: FORM AUTH ---
+const checkAuth = (req, res, next) => {
+ if (!AUTH_USER || !AUTH_PASS) return next();
+
+ const cookies = parseCookies(req);
+ if (cookies.auth_session === SESSION_SECRET) {
+ return next();
+ }
+
+ if (req.path.startsWith('/api')) {
+ res.status(401).send('Unauthorized');
+ } else {
+ res.redirect('/login');
+ }
+};
+
+app.use(checkAuth);
+
+// 6. PROTECTED ROUTE: Main Page (QR Code)
+app.get('/', async (req, res) => {
+ const logoutBtn = `<a href="/logout" style="position: absolute; top: 10px; right: 10px; padding: 8px 16px; background: #ff4444; color: white; text-decoration: none; border-radius: 4px; font-size: 14px;">Logout</a>`;
+
+ if (isConnected) {
+ return res.send(`
+ <html>
+ <body style="font-family: sans-serif; text-align: center; padding-top: 50px; background-color: #f0f2f5;">
+ ${logoutBtn}
+ <div style="background: white; padding: 40px; border-radius: 10px; display: inline-block; box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
+ <h2 style="color: green;">System Operational</h2>
+ <p style="color: #555;">Connected to WhatsApp.</p>
+ <p style="color: #999; font-size: 12px;">Back-end Service</p>
+ </div>
+ </body>
+ </html>
+ `);
+ }
+
+ if (qrCodeData) {
+ try {
+ const qrImage = await QRCode.toDataURL(qrCodeData);
+ return res.send(`
+ <html>
+ <head><meta http-equiv="refresh" content="5"></head>
+ <body style="font-family: sans-serif; text-align: center; padding-top: 50px; background-color: #f0f2f5;">
+ ${logoutBtn}
+ <div style="background: white; padding: 40px; border-radius: 10px; display: inline-block; box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
+ <h2>Scan to Link</h2>
+ <img src="${qrImage}" alt="QR Code" />
+ <p style="color: #666;">Refreshes every 5 seconds...</p>
+ </div>
+ </body>
+ </html>
+ `);
+ } catch (e) {
+ return res.send("Error generating QR.");
+ }
+ }
+
+ return res.send(`
+ <html>
+ <head><meta http-equiv="refresh" content="2"></head>
+ <body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
+ <p>Initializing... please refresh.</p>
+ ${logoutBtn}
+ </body>
+ </html>
+ `);
+});
+
+// --- START SERVER ---
+app.listen(PORT, () => {
+ startWhatsApp();
+ console.log(`Server running on port ${PORT}`);
+
+ if (AUTH_USER && AUTH_PASS) {
+ console.log("Security: Form Authentication is ENABLED.");
+ } else {
+ console.log("Security: Form Authentication is DISABLED (Env vars missing).");
+ }
+});
diff --git a/package.json b/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "whatsapp-logger-backend",
+ "version": "1.0.0",
+ "description": "Backend for WhatsApp Logger using Baileys and Firebase",
+ "main": "index.js",
+ "scripts": {
+ "start": "node index.js"
+ },
+ "dependencies": {
+ "@whiskeysockets/baileys": "^6.6.0",
+ "express": "^4.18.2",
+ "firebase-admin": "^11.11.1",
+ "pino": "^8.17.2",
+ "qrcode": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "author": "Amit",
+ "license": "MIT"
+}+
\ No newline at end of file