diff --git a/src/core/engines/noweb/noweb.exceptions.ts b/src/core/engines/noweb/noweb.exceptions.ts new file mode 100644 index 00000000..a5a74ce9 --- /dev/null +++ b/src/core/engines/noweb/noweb.exceptions.ts @@ -0,0 +1,12 @@ +import { UnprocessableEntityException } from '@nestjs/common'; + +export class NoLastMessageInChatException extends UnprocessableEntityException { + constructor(chatId: string) { + super( + `No recent message found in chat '${chatId}' to modify it. ` + + 'Enable NOWEB store with "config.noweb.store.full_sync=True" when starting a new session, ' + + 'or wait for a message in the chat and try again. ' + + 'Read more: https://waha.devlike.pro/docs/engines/noweb#store', + ); + } +} diff --git a/src/core/engines/noweb/session.noweb.core.ts b/src/core/engines/noweb/session.noweb.core.ts index 7849360d..78bec88d 100644 --- a/src/core/engines/noweb/session.noweb.core.ts +++ b/src/core/engines/noweb/session.noweb.core.ts @@ -67,6 +67,7 @@ import { } from '@waha/core/engines/noweb/noweb.newsletter'; import { NowebAuthFactoryCore } from '@waha/core/engines/noweb/NowebAuthFactoryCore'; import { NowebInMemoryStore } from '@waha/core/engines/noweb/store/NowebInMemoryStore'; +import { NoLastMessageInChatException } from '@waha/core/engines/noweb/noweb.exceptions'; import { NotImplementedByEngineError } from '@waha/core/exceptions'; import { toVcardV3 } from '@waha/core/vcard'; import { createAgentProxy } from '@waha/core/helpers.proxy'; @@ -1863,6 +1864,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { ): Promise { const jid = await this.hooks.wid.chat.promise(chatId, 'chatsPutArchive'); const messages = await this.store.getMessagesByJid(jid, {}, { limit: 1 }); + if (messages.length === 0) { + throw new NoLastMessageInChatException(chatId); + } return await this.sock.chatModify( { archive: archive, lastMessages: messages }, jid, @@ -1883,6 +1887,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { public async chatsUnreadChat(chatId: string): Promise { const jid = await this.hooks.wid.chat.promise(chatId, 'chatsUnreadChat'); const messages = await this.store.getMessagesByJid(jid, {}, { limit: 1 }); + if (messages.length === 0) { + throw new NoLastMessageInChatException(chatId); + } return await this.sock.chatModify( { markRead: false, lastMessages: messages }, jid, diff --git a/src/core/engines/noweb/store/NowebInMemoryStore.ts b/src/core/engines/noweb/store/NowebInMemoryStore.ts index 30ac3553..4d8a3086 100644 --- a/src/core/engines/noweb/store/NowebInMemoryStore.ts +++ b/src/core/engines/noweb/store/NowebInMemoryStore.ts @@ -2,6 +2,7 @@ import type { Chat, Contact, GroupMetadata, proto } from '@adiwajshing/baileys'; import type makeWASocket from '@adiwajshing/baileys'; import type { Label } from '@adiwajshing/baileys/lib/Types/Label'; import { BadRequestException } from '@nestjs/common'; +import { AckToStatus } from '@waha/core/utils/acks'; import { GetChatMessagesFilter, OverviewFilter, @@ -10,8 +11,10 @@ import { LidToPhoneNumber } from '@waha/structures/lids.dto'; import { LimitOffsetParams, PaginationParams, + SortOrder, } from '@waha/structures/pagination.dto'; import { PaginatorInMemory } from '@waha/utils/Paginator'; +import * as lodash from 'lodash'; import { INowebStore } from './INowebStore'; import makeInMemoryStore from './memory/make-in-memory-store'; @@ -56,13 +59,49 @@ export class NowebInMemoryStore implements INowebStore { return this.store.loadMessage(jid, id); } - getMessagesByJid( + async getMessagesByJid( chatId: string, filter: GetChatMessagesFilter, pagination: PaginationParams, merge?: boolean, ): Promise { - throw new BadRequestException(this.errorMessage); + let messages = await this.store.loadMessages( + chatId, + Number.MAX_SAFE_INTEGER, + undefined, + ); + const lte = filter['filter.timestamp.lte']; + const gte = filter['filter.timestamp.gte']; + const fromMe = filter['filter.fromMe']; + const ack = filter['filter.ack']; + messages = messages.filter((msg) => { + const timestamp = lodash.toNumber(msg.messageTimestamp as any); + if (lte != null && timestamp > lte) { + return false; + } + if (gte != null && timestamp < gte) { + return false; + } + if (fromMe != null && msg.key.fromMe !== fromMe) { + return false; + } + if (ack != null && msg.status !== AckToStatus(ack)) { + return false; + } + return true; + }); + // Newest first by default, same as the persistent store + const order = pagination.sortOrder || SortOrder.DESC; + messages = lodash.orderBy( + messages, + [(msg) => lodash.toNumber(msg.messageTimestamp as any)], + [order], + ); + const paginator = new PaginatorInMemory({ + limit: pagination.limit, + offset: pagination.offset, + }); + return paginator.apply(messages); } getMessageById(