feat: ability to filter chats and messages
This commit is contained in:
parent
62860a658c
commit
dd886a1fea
5 changed files with 274 additions and 29 deletions
|
|
@ -360,6 +360,56 @@ const getChats = async (req, res) => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all chats for the given session ID with optional search parameters.
|
||||
*
|
||||
* @function
|
||||
* @async
|
||||
*
|
||||
* @param {Object} req - The request object.
|
||||
* @param {string} req.params.sessionId - The session ID.
|
||||
* @param {Object} res - The response object.
|
||||
*
|
||||
* @returns {Promise<void>} A Promise that resolves when the operation is complete.
|
||||
*
|
||||
* @throws {Error} If the operation fails, an error is thrown.
|
||||
*/
|
||||
const getChatsWithSearch = async (req, res) => {
|
||||
/*
|
||||
#swagger.summary = 'Get all current chats with optional search parameters'
|
||||
#swagger.requestBody = {
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
searchOptions: {
|
||||
properties: {
|
||||
since: {
|
||||
type: 'number',
|
||||
description: 'Timestamp to filter chats since',
|
||||
example: 1746618541
|
||||
},
|
||||
unread: {
|
||||
type: 'boolean',
|
||||
description: 'Filter for unread chats',
|
||||
example: true
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
*/
|
||||
try {
|
||||
const { searchOptions = {} } = req.body
|
||||
const client = sessions.get(req.params.sessionId)
|
||||
const chats = await client.getChats(searchOptions)
|
||||
res.json({ success: true, chats })
|
||||
} catch (error) {
|
||||
sendErrorResponse(res, 500, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the profile picture URL for a given contact ID.
|
||||
*
|
||||
|
|
@ -2170,6 +2220,7 @@ module.exports = {
|
|||
getChatById,
|
||||
getChatLabels,
|
||||
getChats,
|
||||
getChatsWithSearch,
|
||||
getChatsByLabelId,
|
||||
getCommonGroups,
|
||||
getContactById,
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ clientRouter.post('/getBlockedContacts/:sessionId', [middleware.sessionNameValid
|
|||
clientRouter.post('/getChatById/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], clientController.getChatById)
|
||||
clientRouter.post('/getChatLabels/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], clientController.getChatLabels)
|
||||
clientRouter.get('/getChats/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], clientController.getChats)
|
||||
clientRouter.post('/getChats/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], clientController.getChatsWithSearch)
|
||||
clientRouter.post('/getChatsByLabelId/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], clientController.getChatsByLabelId)
|
||||
clientRouter.post('/getCommonGroups/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], clientController.getCommonGroups)
|
||||
clientRouter.post('/getContactById/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], clientController.getContactById)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const fs = require('fs')
|
|||
const path = require('path')
|
||||
const sessions = new Map()
|
||||
const { baseWebhookURL, sessionFolderPath, maxAttachmentSize, setMessagesAsSeen, webVersion, webVersionCacheType, recoverSessions, chromeBin, headless, releaseBrowserLock } = require('./config')
|
||||
const { triggerWebhook, waitForNestedObject, isEventEnabled, sendMessageSeenStatus, sleep } = require('./utils')
|
||||
const { triggerWebhook, waitForNestedObject, isEventEnabled, sendMessageSeenStatus, sleep, patchWWebLibrary } = require('./utils')
|
||||
const { logger } = require('./logger')
|
||||
const { initWebSocketServer, terminateWebSocketServer, triggerWebSocket } = require('./websocket')
|
||||
|
||||
|
|
@ -177,36 +177,12 @@ const setupSession = async (sessionId) => {
|
|||
}
|
||||
|
||||
try {
|
||||
await client.initialize()
|
||||
// hotfix for https://github.com/pedroslopez/whatsapp-web.js/pull/3703
|
||||
client.once('ready', () => {
|
||||
client.pupPage.evaluate(() => {
|
||||
window.Store.FindOrCreateChat = window.require('WAWebFindChatAction')
|
||||
window.WWebJS.getChat = async (chatId, { getAsModel = true } = {}) => {
|
||||
const isChannel = /@\w*newsletter\b/.test(chatId)
|
||||
const chatWid = window.Store.WidFactory.createWid(chatId)
|
||||
let chat
|
||||
|
||||
if (isChannel) {
|
||||
try {
|
||||
chat = window.Store.NewsletterCollection.get(chatId)
|
||||
if (!chat) {
|
||||
await window.Store.ChannelUtils.loadNewsletterPreviewChat(chatId)
|
||||
chat = await window.Store.NewsletterCollection.find(chatWid)
|
||||
}
|
||||
} catch (err) {
|
||||
chat = null
|
||||
}
|
||||
} else {
|
||||
chat = window.Store.Chat.get(chatWid) || (await window.Store.FindOrCreateChat.findOrCreateLatestChat(chatWid))?.chat
|
||||
}
|
||||
|
||||
return getAsModel && chat
|
||||
? await window.WWebJS.getChatModel(chat, { isChannel })
|
||||
: chat
|
||||
}
|
||||
patchWWebLibrary(client).catch((err) => {
|
||||
logger.error({ sessionId, err }, 'Failed to patch WWebJS library')
|
||||
})
|
||||
})
|
||||
await client.initialize()
|
||||
} catch (error) {
|
||||
logger.error({ sessionId, err: error }, 'Initialize error')
|
||||
throw error
|
||||
|
|
|
|||
103
src/utils.js
103
src/utils.js
|
|
@ -1,6 +1,9 @@
|
|||
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) => {
|
||||
|
|
@ -73,6 +76,103 @@ const exposeFunctionIfAbsent = async (page, name, fn) => {
|
|||
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))
|
||||
)
|
||||
}
|
||||
|
||||
// hotfix for https://github.com/pedroslopez/whatsapp-web.js/pull/3703
|
||||
window.Store.FindOrCreateChat = window.require('WAWebFindChatAction')
|
||||
window.WWebJS.getChat = async (chatId, { getAsModel = true } = {}) => {
|
||||
const isChannel = /@\w*newsletter\b/.test(chatId)
|
||||
const chatWid = window.Store.WidFactory.createWid(chatId)
|
||||
let chat
|
||||
|
||||
if (isChannel) {
|
||||
try {
|
||||
chat = window.Store.NewsletterCollection.get(chatId)
|
||||
if (!chat) {
|
||||
await window.Store.ChannelUtils.loadNewsletterPreviewChat(chatId)
|
||||
chat = await window.Store.NewsletterCollection.find(chatWid)
|
||||
}
|
||||
} catch (err) {
|
||||
chat = null
|
||||
}
|
||||
} else {
|
||||
chat = window.Store.Chat.get(chatWid) || (await window.Store.FindOrCreateChat.findOrCreateLatestChat(chatWid))?.chat
|
||||
}
|
||||
|
||||
return getAsModel && chat
|
||||
? await window.WWebJS.getChatModel(chat, { isChannel })
|
||||
: chat
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
triggerWebhook,
|
||||
sendErrorResponse,
|
||||
|
|
@ -81,5 +181,6 @@ module.exports = {
|
|||
sendMessageSeenStatus,
|
||||
decodeBase64,
|
||||
sleep,
|
||||
exposeFunctionIfAbsent
|
||||
exposeFunctionIfAbsent,
|
||||
patchWWebLibrary
|
||||
}
|
||||
|
|
|
|||
116
swagger.json
116
swagger.json
|
|
@ -1604,6 +1604,122 @@
|
|||
"apiKeyAuth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"Client"
|
||||
],
|
||||
"summary": "Get all current chats with optional search parameters",
|
||||
"description": "",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "sessionId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Unique identifier for the session (alphanumeric and - allowed)",
|
||||
"example": "f8377d8d-a589-4242-9ba6-9486a04ef80c"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ForbiddenResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NotFoundResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Unprocessable Entity.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Server failure.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"apiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"searchOptions": {
|
||||
"properties": {
|
||||
"since": {
|
||||
"type": "number",
|
||||
"description": "Timestamp to filter chats since",
|
||||
"example": 1746618541
|
||||
},
|
||||
"unread": {
|
||||
"type": "boolean",
|
||||
"description": "Filter for unread chats",
|
||||
"example": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"application/xml": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"searchOptions": {
|
||||
"properties": {
|
||||
"since": {
|
||||
"type": "number",
|
||||
"description": "Timestamp to filter chats since",
|
||||
"example": 1746618541
|
||||
},
|
||||
"unread": {
|
||||
"type": "boolean",
|
||||
"description": "Filter for unread chats",
|
||||
"example": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/client/getChatsByLabelId/{sessionId}": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue