wwebjs-api/src/utils.js
Nir f1ff7812c3
Replace window.Store in utils.getChats (#119)
This fixes #116 by adhering to the newer whatsapp-web.js interface and using 'window.require' instead of 'window.store'

Co-authored-by: nir <nir@severin>
2026-03-11 14:33:14 +02:00

164 lines
5.5 KiB
JavaScript

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
}
if (searchOptions && searchOptions.messageId !== undefined && m.id.id != searchOptions.messageId) {
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.require('WAWebChatLoadMessages')).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.require('WAWebCollections').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
}