diff --git a/.changeset/polite-foxes-repair.md b/.changeset/polite-foxes-repair.md new file mode 100644 index 0000000000000..2f524c7e5f105 --- /dev/null +++ b/.changeset/polite-foxes-repair.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Added a method to the Apps-Engine that allows apps to read multiple messages from a room diff --git a/apps/meteor/app/apps/server/bridges/rooms.ts b/apps/meteor/app/apps/server/bridges/rooms.ts index 86817c5721e2b..344acc74bda43 100644 --- a/apps/meteor/app/apps/server/bridges/rooms.ts +++ b/apps/meteor/app/apps/server/bridges/rooms.ts @@ -1,11 +1,13 @@ import type { IAppServerOrchestrator } from '@rocket.chat/apps'; -import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; +import type { IMessage, IMessageRaw } from '@rocket.chat/apps-engine/definition/messages'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; +import type { GetMessagesOptions } from '@rocket.chat/apps-engine/server/bridges/RoomBridge'; import { RoomBridge } from '@rocket.chat/apps-engine/server/bridges/RoomBridge'; -import type { ISubscription, IUser as ICoreUser, IRoom as ICoreRoom } from '@rocket.chat/core-typings'; -import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; +import type { ISubscription, IUser as ICoreUser, IRoom as ICoreRoom, IMessage as ICoreMessage } from '@rocket.chat/core-typings'; +import { Subscriptions, Users, Rooms, Messages } from '@rocket.chat/models'; +import type { FindOptions, Sort } from 'mongodb'; import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; import { createDiscussion } from '../../../discussion/server/methods/createDiscussion'; @@ -103,6 +105,38 @@ export class AppRoomBridge extends RoomBridge { return this.orch.getConverters()?.get('users').convertById(room.u._id); } + protected async getMessages(roomId: string, options: GetMessagesOptions, appId: string): Promise { + this.orch.debugLog(`The App ${appId} is getting the messages of the room: "${roomId}" with options:`, options); + + const { limit, skip = 0, sort: _sort } = options; + + const messageConverter = this.orch.getConverters()?.get('messages'); + if (!messageConverter) { + throw new Error('Message converter not found'); + } + + // We support only one field for now + const sort: Sort | undefined = _sort?.createdAt ? { ts: _sort.createdAt } : undefined; + + const messageQueryOptions: FindOptions = { + limit, + skip, + sort, + }; + + const query = { + rid: roomId, + _hidden: { $ne: true }, + t: { $exists: false }, + }; + + const cursor = Messages.find(query, messageQueryOptions); + + const messagePromises: Promise[] = await cursor.map((message) => messageConverter.convertMessageRaw(message)).toArray(); + + return Promise.all(messagePromises); + } + protected async getMembers(roomId: string, appId: string): Promise> { this.orch.debugLog(`The App ${appId} is getting the room's members by room id: "${roomId}"`); const subscriptions = await Subscriptions.findByRoomId(roomId, {}); @@ -220,12 +254,4 @@ export class AppRoomBridge extends RoomBridge { const members = await Users.findUsersByUsernames(usernames, { limit: 50 }).toArray(); await Promise.all(members.map((user) => removeUserFromRoom(roomId, user))); } - - protected getMessages( - _roomId: string, - _options: { limit: number; skip?: number; sort?: Record }, - _appId: string, - ): Promise { - throw new Error('Method not implemented.'); - } } diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 9804e0bb3b2c2..199722a0d8961 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -17,6 +17,40 @@ export class AppMessagesConverter { return this.convertMessage(msg); } + async convertMessageRaw(msgObj) { + if (!msgObj) { + return undefined; + } + + const { attachments, ...message } = msgObj; + const getAttachments = async () => this._convertAttachmentsToApp(attachments); + + const map = { + id: '_id', + threadId: 'tmid', + reactions: 'reactions', + parseUrls: 'parseUrls', + text: 'msg', + createdAt: 'ts', + updatedAt: '_updatedAt', + editedAt: 'editedAt', + emoji: 'emoji', + avatarUrl: 'avatar', + alias: 'alias', + file: 'file', + customFields: 'customFields', + groupable: 'groupable', + token: 'token', + blocks: 'blocks', + roomId: 'rid', + editor: 'editedBy', + attachments: getAttachments, + sender: 'u', + }; + + return transformMappedData(message, map); + } + async convertMessage(msgObj) { if (!msgObj) { return undefined; diff --git a/packages/apps/src/AppsEngine.ts b/packages/apps/src/AppsEngine.ts index 856bc12537905..b85672d23f5a4 100644 --- a/packages/apps/src/AppsEngine.ts +++ b/packages/apps/src/AppsEngine.ts @@ -8,6 +8,7 @@ export type { IVisitorPhone as IAppsVisitorPhone, } from '@rocket.chat/apps-engine/definition/livechat'; export type { IMessage as IAppsMessage } from '@rocket.chat/apps-engine/definition/messages'; +export type { IMessageRaw as IAppsMesssageRaw } from '@rocket.chat/apps-engine/definition/messages'; export { AppInterface as AppEvents } from '@rocket.chat/apps-engine/definition/metadata'; export type { IUser as IAppsUser } from '@rocket.chat/apps-engine/definition/users'; export type { IRole as IAppsRole } from '@rocket.chat/apps-engine/definition/roles'; diff --git a/packages/apps/src/converters/IAppMessagesConverter.ts b/packages/apps/src/converters/IAppMessagesConverter.ts index 185e247895de2..863c10c954777 100644 --- a/packages/apps/src/converters/IAppMessagesConverter.ts +++ b/packages/apps/src/converters/IAppMessagesConverter.ts @@ -1,6 +1,6 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import type { IAppsMessage } from '../AppsEngine'; +import type { IAppsMessage, IAppsMesssageRaw } from '../AppsEngine'; export interface IAppMessagesConverter { convertById(messageId: IMessage['_id']): Promise; @@ -10,4 +10,6 @@ export interface IAppMessagesConverter { convertAppMessage(message: undefined | null): Promise; convertAppMessage(message: IAppsMessage): Promise; convertAppMessage(message: IAppsMessage | undefined | null): Promise; + convertMessageRaw(message: IMessage): Promise; + convertMessageRaw(message: IMessage | undefined | null): Promise; } diff --git a/yarn.lock b/yarn.lock index e7555d99208ed..6d962b0844935 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8510,8 +8510,8 @@ __metadata: linkType: soft "@rocket.chat/apps-engine@npm:alpha": - version: 1.44.0-alpha.818 - resolution: "@rocket.chat/apps-engine@npm:1.44.0-alpha.818" + version: 1.44.0-alpha.828 + resolution: "@rocket.chat/apps-engine@npm:1.44.0-alpha.828" dependencies: "@msgpack/msgpack": 3.0.0-beta2 adm-zip: ^0.5.9 @@ -8527,7 +8527,7 @@ __metadata: uuid: ~8.3.2 peerDependencies: "@rocket.chat/ui-kit": "*" - checksum: acef47bc7f13e0682d018531638b5168c3acd59beae37b013e881ea6fadfe12670fe10545f4a89487f7bedbe9166028833cba7ed3fc401d4283327e47e00e61c + checksum: e26914b62d2e9823577fe8165a2635d65f69ddc315a880bbc417ddd674e4df487dc9bc9507bf3a0616de06cd927596872c1e90e4c29c61da8581e0a1b7c8d97d languageName: node linkType: hard