const axios = require('axios') const { globalApiKey, disabledCallbacks, enableWebHook } = require('./config') const { logger } = require('./logger') const ChatFactory = require('whatsapp-web.js/src/factories/ChatFactory') const Client = require('whatsapp-web.js').Client const { Chat, Message } = require('whatsapp-web.js/src/structures') // Trigger webhook endpoint const triggerWebhook = (webhookURL, sessionId, dataType, data) => { if (enableWebHook) { axios.post(webhookURL, { dataType, data, sessionId }, { headers: { 'x-api-key': globalApiKey } }) .then(() => logger.debug({ sessionId, dataType, data: data || '' }, `Webhook message sent to ${webhookURL}`)) .catch(error => logger.error({ sessionId, dataType, err: error, data: data || '' }, `Failed to send webhook message to ${webhookURL}`)) } } // Function to send a response with error status and message const sendErrorResponse = (res, status, message) => { res.status(status).json({ success: false, error: message }) } // Function to wait for a specific item not to be null const waitForNestedObject = (rootObj, nestedPath, maxWaitTime = 10000, interval = 100) => { const start = Date.now() return new Promise((resolve, reject) => { const checkObject = () => { const nestedObj = nestedPath.split('.').reduce((obj, key) => obj ? obj[key] : undefined, rootObj) if (nestedObj) { // Nested object exists, resolve the promise resolve() } else if (Date.now() - start > maxWaitTime) { // Maximum wait time exceeded, reject the promise logger.error('Timed out waiting for nested object') reject(new Error('Timeout waiting for nested object')) } else { // Nested object not yet created, continue waiting setTimeout(checkObject, interval) } } checkObject() }) } const isEventEnabled = (event) => { return !disabledCallbacks.includes(event) } const sendMessageSeenStatus = async (message) => { try { const chat = await message.getChat() await chat.sendSeen() } catch (error) { logger.error(error, 'Failed to send seen status') } } const decodeBase64 = function * (base64String) { const chunkSize = 1024 for (let i = 0; i < base64String.length; i += chunkSize) { const chunk = base64String.slice(i, i + chunkSize) yield Buffer.from(chunk, 'base64') } } const sleep = function (ms) { return new Promise(resolve => setTimeout(resolve, ms)) } const exposeFunctionIfAbsent = async (page, name, fn) => { const exist = await page.evaluate((name) => { return !!window[name] }, name) if (exist) { return } await page.exposeFunction(name, fn) } const patchWWebLibrary = async (client) => { // MUST be run after the 'ready' event fired Client.prototype.getChats = async function (searchOptions = {}) { const chats = await this.pupPage.evaluate(async (searchOptions) => { return await window.WWebJS.getChats({ ...searchOptions }) }, searchOptions) return chats.map(chat => ChatFactory.create(this, chat)) } Chat.prototype.fetchMessages = async function (searchOptions) { const messages = await this.client.pupPage.evaluate(async (chatId, searchOptions) => { const msgFilter = (m) => { if (m.isNotification) { return false } if (searchOptions && searchOptions.fromMe !== undefined && m.id.fromMe !== searchOptions.fromMe) { return false } if (searchOptions && searchOptions.since !== undefined && Number.isFinite(searchOptions.since) && m.t < searchOptions.since) { return false } return true } const chat = await window.WWebJS.getChat(chatId, { getAsModel: false }) let msgs = chat.msgs.getModelsArray().filter(msgFilter) if (searchOptions && searchOptions.limit > 0) { while (msgs.length < searchOptions.limit) { const loadedMessages = await window.Store.ConversationMsgs.loadEarlierMsgs(chat) if (!loadedMessages || !loadedMessages.length) break msgs = [...loadedMessages.filter(msgFilter), ...msgs] } if (msgs.length > searchOptions.limit) { msgs.sort((a, b) => (a.t > b.t) ? 1 : -1) msgs = msgs.splice(msgs.length - searchOptions.limit) } } return msgs.map(m => window.WWebJS.getMessageModel(m)) }, this.id._serialized, searchOptions) return messages.map(m => new Message(this.client, m)) } await client.pupPage.evaluate(() => { // hotfix for https://github.com/pedroslopez/whatsapp-web.js/pull/3643 window.WWebJS.getChats = async (searchOptions = {}) => { const chatFilter = (c) => { if (searchOptions && searchOptions.unread === true && c.unreadCount === 0) { return false } if (searchOptions && searchOptions.since !== undefined && Number.isFinite(searchOptions.since) && c.t < searchOptions.since) { return false } return true } const allChats = window.Store.Chat.getModelsArray() const filteredChats = allChats.filter(chatFilter) return await Promise.all( filteredChats.map(chat => window.WWebJS.getChatModel(chat)) ) } }) } module.exports = { triggerWebhook, sendErrorResponse, waitForNestedObject, isEventEnabled, sendMessageSeenStatus, decodeBase64, sleep, exposeFunctionIfAbsent, patchWWebLibrary }