WhatsApp-Logger-Self-Hosted-

A privacy-focused, self-hosted...
Log | Files | Refs | README | LICENSE

commit b8bd6b4d89793ac3ea46dfdd9c30c58fef4b536d
parent 38fb84fddd343ebfc12d2849763651e6d58614ed
Author: AMIT DUTTA <amitdutta4255@gmail.com>
Date:   Mon, 16 Feb 2026 20:12:33 +0530

Add files via upload
Diffstat:
ACODE_OF_CONDUCT.md | 19+++++++++++++++++++
ACONTRIBUTING.md | 19+++++++++++++++++++
ADockerfile | 30++++++++++++++++++++++++++++++
ASECURITY.md | 25+++++++++++++++++++++++++
Aindex.js | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackage.json | 22++++++++++++++++++++++
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
© notamitgamer • Site Built: 2026-07-21 13:58:23 UTC • git-mirror commit: 1037f62 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror