pedroslopez / whatsapp-web.js

A WhatsApp client library for NodeJS that connects through the WhatsApp Web browser app
https://wwebjs.dev
Apache License 2.0
15.39k stars 3.67k forks source link

Two chrome instance or process appeared in top command process list #2742

Closed NitinNR closed 9 months ago

NitinNR commented 9 months ago

Is there an existing issue for this?

Describe the bug

Two chrome instance or process appeared in top command process list

Expected behavior

it should only creates one chrome process

Steps to Reproduce the Bug or Issue

just run nodejs code in pm2

Relevant Code

const { Client, MessageMedia, LocalAuth, NoAuth } = require('whatsapp-web.js'); const express = require('express'); const { body, validationResult } = require('express-validator'); const socketIO = require('socket.io'); const qrcode = require('qrcode'); const http = require('http'); const fs = require('fs'); // const { phoneNumberFormatter } = require('./helpers/formatter'); const fileUpload = require('express-fileupload'); const axios = require('axios');

//change const my_app_name = "nrtest" const app = express(); const server = http.createServer(app); const io = socketIO(server, { path: "/" + my_app_name + "/socket.io" });

const redis = require('redis'); const redis_client = redis.createClient();

const connect_redis = async () => {

await redis_client.on('error', (err) => {
    console.error(`Redis Error: ${err}`);
}).connect();

console.log(await redis_client.ping());

}

connect_redis()

// Test the connection

const setRedisKeyValue = async (key, value) => { console.log(await redis_client.set(key, value)); }

const getRedisKeyValue = async (key) => { return await redis_client.get(key); }

// create cdn folder

fs.mkdir(cdn/${my_app_name}, (err) => { if (err) { console.log(err); } else { console.log('Folder created successfully!'); } });

//const port = process.env.PORT || 7017; //change const port = 7000; const webhook_url = "localhost"; const cb_port = 4002

// NewEraLiFe

app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(fileUpload({ // debug: true }));

app.use(express.static('cdn/' + my_app_name));

var PROCESS_MEDIA_MESSAGE = function (data, filename) { // var SANITIZEd = SANITIZE_MSG(port,data) //const http = require("http") // console.log("filename:", filename); const new_data = JSON.stringify({ "data": data, "filename": filename, "app_name": my_app_name }) // const new_data = SANITIZEd

const options = {
    hostname: webhook_url,
    port: cb_port,
    path: "/bot/wapi",
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "Content-Length": Buffer.byteLength(new_data, 'utf8'),
    },
}
const req = http.request(options, res => {
    console.log(`statusCode M: ${res.statusCode}`);
    res.on('data', d => {
        process.stdout.write(d)
    })
})
req.write(new_data)
req.end()

};

var SANITIZE_ACK = function (data) { return JSON.stringify({ ack: [{ id: data.id._serialized, from: data.id.remote, mdata: data, status: (data.ack == 1 ? 'sent' : (data.ack == 2 ? 'delivered' : 'viewed')) }], instanceId: port }); };

var PROCESS_MESSAGE = function (data) { // var SANITIZEd = SANITIZE_MSG(port,data) //const http = require("http") const new_data = JSON.stringify({ "data": data, "app_name": my_app_name }) // const new_data = SANITIZEd // console.log("sending", new_data) const options = { hostname: webhook_url, //"en6gta6u8epkt.x.pipedream.net", port: cb_port, //443, path: "/bot/wapi", method: "POST", bodu: new_data, headers: { "Content-Type": "application/json", // "Content-Length": new_data.length, "Content-Length": Buffer.byteLength(new_data, 'utf8'), }, } const req = http.request(options, res => { console.log(statusCode text: ${res.statusCode})

    res.on('data', d => {
        process.stdout.write(d)
    })
})
req.on('error', (error) => {
    console.error('Error sending request:', error);
});
// StringEntity new_data = new StringEntity(new_data, "UTF-8");
console.log(new_data);
req.write(new_data)
req.end()

};

//Here will receive our our message var PROCESS_ACK = function (data) { //const http = require("http") // var SANITIZED = SANITIZE_ACK(data); console.log("data.hasMedia:", data.hasMedia); console.log("data.hasMedia:", data.isStatus);

if (!data.isStatus) {

    if (data.hasMedia) {
        downloadStoreMedia(data)
    } else {
        const new_data = JSON.stringify({ "data": data, "app_name": my_app_name })
        const options = {
            hostname: webhook_url,
            port: cb_port,
            bodu: new_data,
            path: "/bot/wapi",
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "Content-Length": Buffer.byteLength(new_data, 'utf8'),
            },
        }
        const req = http.request(options, res => {
            console.log(`statusCode ack: ${res.statusCode}`)
            res.on('data', d => {
                process.stdout.write(d)
            })
        })
        req.write(new_data)
        req.end()

    }
}

};

var PROCESS_STATE = function (data) { // console.log("[STATE CHANGED] -", data); };

// const SESSION_FILE_PATH = ('/home/ketan/Desktop/l/whatsapp-api/Whats3API/Whats3API5/WhatsSessions/'+ port + '.json'); // let sessionCfg; // if (fs.existsSync(SESSION_FILE_PATH)) { // sessionCfg = require(SESSION_FILE_PATH); // }

app.get('/' + my_app_name + '', (req, res) => { res.sendFile('index.html', { root: __dirname }); });

let client; const setupClient = async () => {

try {

    client = new Client({
        webVersion: "v2.2402.5",
        webVersionCache: {
            type: 'local'
        },
        takeoverOnConflict:true,
        takeoverTimeoutMs:3000,
        authStrategy: new LocalAuth({ clientId: my_app_name }),
        restartOnAuthFail: true,
        qrMaxRetries: 2,
        puppeteer: {
            headless: true,
            executablePath: '/usr/bin/google-chrome-stable',
            args: [
                '--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-dev-shm-usage',
                '--disable-accelerated-2d-canvas',
                '--no-first-run',
                '--no-zygote',
                '--single-process', // <- this one doesn't works in Windows
                '--disable-gpu',
            ],
        }
    });

    client.on('message', async msg => {
        //CRECKING IF MESSAGE HAVE ANY MEDIA TYPE EMBED
        console.log("message received=>");
        // const new_data = JSON.stringify(msg)
        // console.log(new_data);
        console.log("msg.isStatus=", msg.isStatus);
        if (!msg.isStatus) {
            if (msg.hasMedia) {
                downloadStoreMedia(msg)
            } else {
                //  const chat = await msg.getChat();
                //  // simulates typing in the chat
                //  chat.sendStateTyping();
                PROCESS_MESSAGE(msg);
            }

        }

    });

    client.on('message_ack', (msg, ack) => {
        /*
            == ACK VALUES ==
            ACK_ERROR: -1
            ACK_PENDING: 0
            ACK_SERVER: 1
            ACK_DEVICE: 2
            ACK_READ: 3
            ACK_PLAYED: 4
        */
        console.log("======= ACK =====>", ack);
        const new_data = JSON.stringify(msg)
        console.log(new_data);
        if (msg?._data?.author !== "undefined" && msg?._data?.author && !('ephemeralStartTimestamp' in msg?._data) && ack === 2) {
            console.log("TTTTTTTTTTTTT");
            PROCESS_ACK(msg);
        } else {
            console.log(msg?._data?.author, 'ephemeralStartTimestamp' in msg?._data,);
        }
    });

    client.on('change_state', (state) => {
        PROCESS_STATE(state);
    });

    await client.initialize();

} catch (error) {

    console.log("Client ERRRRor", error)

}

// return client

} console.log(process.pid)

setupClient()

const downloadStoreMedia = async (msg) => {

console.log("media message received");
try {
    const attachmentData = await msg.downloadMedia();
    // console.log("Media received", attachmentData);
    var MimeData = attachmentData.data;
    var MimeType = attachmentData.mimetype;
    var Filename = attachmentData.filename;
    var length = attachmentData.length;
    if (!Filename & msg.type != "sticker" & (msg.from).length < 20) {
        Filename = msg.id.id
        MimeType = MimeType.split("/")[1];
        Filename = Filename + "." + MimeType
    }

    fs.writeFile(process.cwd() + '/cdn/' + my_app_name + "/" + Filename, MimeData, 'base64', function (err) {
        if (err) {
            console.log(err);
            console.log("#Error on saving file");
            PROCESS_MESSAGE(msg);
        } else {

            PROCESS_MEDIA_MESSAGE(msg, Filename);
            console.log("#File SAVED");
        }
    });
} catch (err) {
    console.log(err);
}

}

// Socket IO io.on('connection', async function (socket) { console.log("got connnnn");

socket.emit('message', `${my_app_name} API Connecting...`);

const user_session = await getRedisKeyValue(my_app_name)

console.log("user_session:", user_session);

if (user_session === 'ready') {
    socket.emit('message', 'Whatsapp is authenticated!');
    socket.emit('message', 'Whatsapp is ready!');
} else {

    if (user_session !== "stop_qr") {

        // client.on('qr', async (qr) => {
        //     console.log('QR RECEIVED.1');

        //     qrcode.toDataURL(qr, (err, url) => {
        //         socket.emit('qr', url);
        //         socket.emit('message', 'QR Code received, scan please!');
        //     });
        // });

        // client.on('ready', async (data) => {
        //     await redis_client.set(my_app_name, "ready")
        //     socket.emit('ready', 'Whatsapp is ready!');
        //     socket.emit('message', 'Whatsapp is ready!');
        // });

        // client.on('authenticated', async (session) => {
        //     await setRedisKeyValue(my_app_name, "ready")
        //     socket.emit('authenticated', 'Whatsapp is authenticated!');
        //     socket.emit('message', 'Whatsapp is authenticated!');
        //     console.log("WhatsApp is ready")
        //     // sessionCfg = session;
        //     // fs.writeFile(SESSION_FILE_PATH, JSON.stringify(session), function(err) {
        //     //   if (err) {
        //     //     console.error(err);
        //     //   }
        //     // });
        // });

        // client.on('auth_failure', async function (session) {
        //     console.log("auth_failure !");
        //     await redis_client.set(my_app_name, "notready")
        //     socket.emit('message', 'Auth failure, restarting...');
        // });

        // client.on('disconnected', async (reason) => {
        //     await redis_client.set(my_app_name, "stop_qr")
        //     socket.emit('disconnected', 'Whatsapp is disconnected!');
        //     // await redis_client.set(my_app_name, "notready")
        //     console.log("Dissconnected ! 1");
        //     socket.emit('message', 'Whatsapp is disconnected!');
        //     deleteSessionFile()
        //     // await client.logout();

        //     await client.destroy();

        //     // await client.resetState();
        //     // await client.initialize();
        //     // await client.pupPage.reload();
        // });

    } else {

        socket.emit("message", "Plase Generate New qr code !")

    }

}

socket.on("generate_new_qr", async function (data) {
    socket.emit('message', `Getting New Qr code...`);
    try {
        await client.destroy();
        console.log("browser isConnected:",client.pupBrowser.isConnected());
        console.log('pupBrowser.pages:',(await client.pupBrowser.pages())?.length);
        console.log('pupBrowser.process:',client.pupBrowser.process()?.pid);
        console.log('pupBrowser.browserContexts:',client?.pupBrowser?.browserContexts()?.length);

    } catch (error) {
        console.log("Failed Client Destruction");
    }

    client.on('qr', async (qr) => {
        console.log('QR RECEIVED.');
        console.log("browser isConnected:",client?.pupBrowser?.isConnected());
        console.log('pupBrowser.pages:',(await client?.pupBrowser?.pages())?.length);
        console.log('pupBrowser.process:',client?.pupBrowser?.process()?.pid);
        console.log('pupBrowser.browserContexts:',client?.pupBrowser?.browserContexts()?.length);
        // await redis_client.set(my_app_name, "qr_gen")
        qrcode.toDataURL(qr, (err, url) => {
            socket.emit('qr', url);
            socket.emit('message', 'QR Code received, scan please!');
        });
    });

    client.on('ready', async (data) => {
        await redis_client.set(my_app_name, "ready")

        socket.emit('ready', 'Whatsapp is ready!');
        socket.emit('message', 'Whatsapp is ready!');
    });

    client.on('authenticated', async (session) => {
        await setRedisKeyValue(my_app_name, "ready")
        socket.emit('authenticated', 'Whatsapp is authenticated!');
        socket.emit('message', 'Whatsapp is authenticated!');
        console.log("WhatsApp is ready")
        // sessionCfg = session;
        // fs.writeFile(SESSION_FILE_PATH, JSON.stringify(session), function(err) {
        //   if (err) {
        //     console.error(err);
        //   }
        // });
    });

    client.on('auth_failure', async function (session) {
        console.log("auth_failure !");
        await redis_client.set(my_app_name, "notready")
        socket.emit('message', 'Auth failure, restarting...');
    });

    client.on('disconnected', async (reason) => {
        await redis_client.set(my_app_name, "stop_qr")
        socket.emit('disconnected', 'Whatsapp is disconnected!');
        // await redis_client.set(my_app_name, "notready")
        console.log("Dissconnected !");
        socket.emit('message', 'Whatsapp is disconnected!');
        deleteSessionFile()
        // await client.logout();

        await client.destroy();
        await client.removeListener("qr",()=>{})
        await client.removeListener("disconnected",()=>{})

        // await client.resetState();
        // await client.initialize();
        // await client.pupPage.reload();
    });
    await client.initialize();

})

});

const deleteSessionFile = function () { const mysessionpath = .wwebjs_auth/session-${my_app_name}; fs.rmdir(mysessionpath, { recursive: true }, function (err) { if (err) return console.log(">>>>>>>>ERRRRRR<<<<<<< while deleting session file"); console.log('Session deleted!'); }); }

const checkRegisteredNumber = async function (number) { const isRegistered = await client.isRegisteredUser(number); return isRegistered; }

// Send message app.post('/sendMessage', [ body('phone').notEmpty(), body('body').notEmpty(), ], async (req, res) => { const errors = validationResult(req).formatWith(({ msg }) => { return msg; }); // console.log(req.body); if (!errors.isEmpty()) { return res.status(422).json({ status: false, message: errors.mapped() }); }

// const number = phoneNumberFormatter(req.body.phone);
const number = req.body.phone.replace("+", "")
console.log("number :", number);
const message = req.body.body;

const isRegisteredNumber = await checkRegisteredNumber(number);

if (!isRegisteredNumber) {
    return res.status(422).json({
        status: false,
        message: 'The number is not registered'
    });
}

client.sendMessage(number, message).then(response => {
    res.status(200).json({
        status: true,
        response: response
    });
}).catch(err => {
    console.log("errrrrrrr:", err);
    res.status(500).json({
        status: false,
        response: err
    });
});

});

// Send media app.post('/sendFile', async (req, res) => { // const number = phoneNumberFormatter(req.body.phone); const number = req.body.phone.replace("+", "") console.log(number, "number");

const caption = req.body.caption;
let fileUrl = req.body.file;
const filename = req.body.filename;

console.log(fileUrl);

// const media = MessageMedia.fromFilePath('./image-example.png');
// const file = req.files.file;
// const media = new MessageMedia(file.mimetype, file.data.toString('base64'), file.name);
let mimetype;
try {

    let attachment = await axios.get(fileUrl, {
        rejectUnauthorized: false,
        responseType: 'arraybuffer',
    }).then(response => {
        mimetype = response.headers['content-type'];
        return response.data.toString('base64');
    }).catch(async err => {

        // setTimeout(async() => {

        //   const res_url = await err.response?.request?.res?.responseUrl;
        //   fileUrl = res_url;
        //   console.log("res url=========", fileUrl);
        //   if (res_url) {
        //     console.log("UUUUUUUUUUUUUU");
        //     return await axios.get(res_url).then(contentResponse => {
        //       mimetype = contentResponse.headers['content-type'];
        //       console.log("mimetype", mimetype);
        //       return contentResponse.data.toString('base64');
        //     }).catch(err => console.log("======eeee>", err.message))
        //   } else {
        //     console.log("TTTTTTTTTTTTTT");
        //     // return await axios.get(fileUrl, {
        //     //   maxRedirects: 5, validateStatus: status => status < 400
        //     // }).then(async response2 => {
        //     //   console.log("final Url:", finalUrl);
        //     //   const finalUrl = response2.request.res.responseUrl;
        //     //   return await axios.get(finalUrl, {
        //     //     rejectUnauthorized: false,
        //     //     responseType: 'arraybuffer'
        //     //   }).then(contentResponse => {
        //     //     mimetype = contentResponse.headers['content-type'];
        //     //     return { attachment: contentResponse.data.toString('base64'), mimetype };
        //     //   })
        //     //     .catch(contentError => { console.error("err2221==>", contentError.message) })
        //     // }).catch(error => {
        //     //   console.error("err1112", error.message);
        //     // });

        //   }

        // }, 3000);

        return await getResourceWithRetry(fileUrl)
            .then(data => {
                // Handle the successful response
                console.log(data.status);
                mimetype = data.headers['content-type'];
                return data.data.toString('base64');
            })
            .catch(error => {
                // Handle errors
                console.error(error.message);
            });

    });

    console.log("EEEEEEEEEEEEEEEEEEEE", mimetype);
    const media = new MessageMedia(mimetype, attachment, filename);
    const cap = { caption }

    client.sendMessage(number, media, cap).then(response => {
        console.log("===send file res==");
        res.status(200).json({
            status: true,
            response: response
        });
    }).catch(err => {
        console.log("err2====>", err);
        res.status(500).json({
            status: false,
            response: err
        });
    });

} catch (error) {
    console.log("whatsapp web error for sending file==>", error.message);
    res.status(502).json({
        status: false,
        message: 'whatsapp web error for sending file'
    });

}

});

// app.post('/sendLocalFiles', async (req, res))=>{ // const { MessageMedia } = require('whatsapp-web.js'); // const media = MessageMedia.fromFilePath('/home/ketan/Desktop/lifeel/whatsapp-api/Whats3API/Whats3API5/public/cdn/7015/sample.pdf'); // chat.sendMessage(media); // }

const findGroupByName = async function (name) { const group = await client.getChats().then(chats => { return chats.find(chat => chat.isGroup && chat.name.toLowerCase() == name.toLowerCase() ); }); return group; }

// Send message to group // You can use chatID or group name, yea! app.post('/send-group-message', [ body('id').custom((value, { req }) => { if (!value && !req.body.name) { throw new Error('Invalid value, you can use id or name'); } return true; }), body('message').notEmpty(), ], async (req, res) => { const errors = validationResult(req).formatWith(({ msg }) => { return msg; });

if (!errors.isEmpty()) {
    return res.status(422).json({
        status: false,
        message: errors.mapped()
    });
}

let chatId = req.body.id;
const groupName = req.body.name;
const message = req.body.message;

// Find the group by name
if (!chatId) {
    const group = await findGroupByName(groupName);
    if (!group) {
        return res.status(422).json({
            status: false,
            message: 'No group found with name: ' + groupName
        });
    }
    chatId = group.id._serialized;
}

client.sendMessage(chatId, message).then(response => {
    res.status(200).json({
        status: true,
        response: response
    });
}).catch(err => {
    res.status(500).json({
        status: false,
        response: err
    });
});

});

// my custome functions

function getResourceWithRetry(url, maxRetries = 6, retryDelay = 1000) { function attemptRequest(attempt) { return axios.get(url, { rejectUnauthorized: false, responseType: 'arraybuffer' }) .then(response => { // Check if the response indicates success (status code 2xx) if (response.status >= 200 && response.status < 300) { return response; }

            // If not successful, throw an error to trigger the catch block
            throw new Error(`Unsuccessful response: ${response.status}`);
        })
        .catch(error => {
            // Check if the error is a 404 and there are remaining attempts
            if (error.response && error.response.status === 404 && attempt < maxRetries) {
                console.log(`Resource not found (attempt ${attempt + 1}/${maxRetries}). Retrying in ${retryDelay / 1000} seconds...`);

                // Retry after a delay
                return new Promise(resolve => setTimeout(() => resolve(attemptRequest(attempt + 1)), retryDelay));
            }

            // If not a 404 or no more attempts, reject the Promise with the error
            console.log(">>>>>>>>>>>", error.message);
        });
}

// Start the initial attempt
return attemptRequest(0);

}

server.listen(port, function () { console.log('App running on : ' + port); });

Browser Type

Google Chrome

WhatsApp Account Type

Standard

Does your WhatsApp account have multidevice enabled?

Yes, I am using Multi Device

Environment

Distributor ID: Ubuntu Description: Ubuntu 22.04.3 LTS Release: 22.04 Codename: jammy

latest whatsapp-web.js version

WhatsApp Web version is in the code itself

nodejs version : v16.14.0

Additional context

No response

alechkos commented 9 months ago

How is it related to the library bug?

NitinNR commented 9 months ago

Need to know why its creating two chrome processes ?