From af536a9f4831ac0217f899a35b1e966b6fb666e8 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 11:05:22 +0100 Subject: [PATCH 01/14] feat: zod validation message --- api/src/chat/schemas/types/message.ts | 385 +++++++++++++----- api/src/chat/schemas/types/options.ts | 69 ++-- api/src/chat/schemas/types/quick-reply.ts | 5 +- api/src/chat/services/block.service.ts | 19 +- api/src/chat/validation-rules/is-message.ts | 110 ++--- .../validation-rules/is-valid-message-text.ts | 5 +- .../i18n/services/translation.service.spec.ts | 3 +- api/src/i18n/services/translation.service.ts | 4 +- 8 files changed, 365 insertions(+), 235 deletions(-) diff --git a/api/src/chat/schemas/types/message.ts b/api/src/chat/schemas/types/message.ts index c8c8e14f4..7cf43b0af 100644 --- a/api/src/chat/schemas/types/message.ts +++ b/api/src/chat/schemas/types/message.ts @@ -6,14 +6,24 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ +/* + * Copyright © 2025 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + +import { z } from 'zod'; + import { PluginName } from '@/plugins/types'; import { Message } from '../message.schema'; -import { AttachmentPayload } from './attachment'; -import { Button } from './button'; -import { ContentOptions } from './options'; -import { StdQuickReply } from './quick-reply'; +import { attachmentPayloadSchema } from './attachment'; +import { buttonSchema } from './button'; +import { contentOptionsSchema } from './options'; +import { QuickReplyType, stdQuickReplySchema } from './quick-reply'; /** * StdEventType enum is declared, and currently not used @@ -41,6 +51,10 @@ export enum IncomingMessageType { unknown = '', } +export const incomingMessageType = z.nativeEnum(IncomingMessageType); + +export type IncomingMessageTypeLiteral = z.infer; + export enum OutgoingMessageFormat { text = 'text', quickReplies = 'quickReplies', @@ -50,6 +64,12 @@ export enum OutgoingMessageFormat { carousel = 'carousel', } +export const outgoingMessageFormatSchema = z.nativeEnum(OutgoingMessageFormat); + +export type OutgoingMessageFormatLiteral = z.infer< + typeof outgoingMessageFormatSchema +>; + /** * FileType enum is declared, and currently not used **/ @@ -61,6 +81,10 @@ export enum FileType { unknown = 'unknown', } +export const fileTypeSchema = z.nativeEnum(FileType); + +export type FileTypeLiteral = z.infer; + export enum PayloadType { location = 'location', attachments = 'attachments', @@ -68,85 +92,151 @@ export enum PayloadType { button = 'button', } -export type StdOutgoingTextMessage = { text: string }; +export const payloadTypeSchema = z.nativeEnum(PayloadType); -export type StdOutgoingQuickRepliesMessage = { - text: string; - quickReplies: StdQuickReply[]; -}; +export type PayloadTypeLiteral = z.infer; -export type StdOutgoingButtonsMessage = { - text: string; - buttons: Button[]; -}; +export const stdOutgoingTextMessageSchema = z.object({ + text: z.string(), +}); -export type ContentElement = { id: string; title: string } & Record< - string, - any +export type StdOutgoingTextMessage = z.infer< + typeof stdOutgoingTextMessageSchema >; -export type StdOutgoingListMessage = { - options: ContentOptions; - elements: ContentElement[]; - pagination: { - total: number; - skip: number; - limit: number; - }; -}; - -export type StdOutgoingAttachmentMessage = { - // Stored in DB as `AttachmentPayload`, `Attachment` when populated for channels relaying - attachment: AttachmentPayload; - quickReplies?: StdQuickReply[]; -}; - -export type StdPluginMessage = { - plugin: PluginName; - args: { [key: string]: any }; -}; - -export type BlockMessage = - | string[] - | StdOutgoingTextMessage - | StdOutgoingQuickRepliesMessage - | StdOutgoingButtonsMessage - | StdOutgoingListMessage - | StdOutgoingAttachmentMessage - | StdPluginMessage; - -export type StdOutgoingMessage = - | StdOutgoingTextMessage - | StdOutgoingQuickRepliesMessage - | StdOutgoingButtonsMessage - | StdOutgoingListMessage - | StdOutgoingAttachmentMessage; - -type StdIncomingTextMessage = { text: string }; - -export type StdIncomingPostBackMessage = StdIncomingTextMessage & { - postback: string; -}; - -export type StdIncomingLocationMessage = { - type: PayloadType.location; - coordinates: { - lat: number; - lon: number; - }; -}; - -export type StdIncomingAttachmentMessage = { - type: PayloadType.attachments; - serialized_text: string; - attachment: AttachmentPayload | AttachmentPayload[]; -}; - -export type StdIncomingMessage = - | StdIncomingTextMessage - | StdIncomingPostBackMessage - | StdIncomingLocationMessage - | StdIncomingAttachmentMessage; +export const stdOutgoingQuickRepliesMessageSchema = z.object({ + text: z.string(), + quickReplies: z.array(stdQuickReplySchema), +}); + +export type StdOutgoingQuickRepliesMessage = z.infer< + typeof stdOutgoingQuickRepliesMessageSchema +>; + +export const stdOutgoingButtonsMessageSchema = z.object({ + text: z.string(), + buttons: z.array(buttonSchema), +}); + +export type StdOutgoingButtonsMessage = z.infer< + typeof stdOutgoingButtonsMessageSchema +>; + +export const contentElementSchema = z + .object({ + id: z.string(), + title: z.string(), + }) + .catchall(z.any()); + +export type ContentElement = z.infer; + +export const stdOutgoingListMessageSchema = z.object({ + options: contentOptionsSchema, + elements: z.array(contentElementSchema), + pagination: z.object({ + total: z.number(), + skip: z.number(), + limit: z.number(), + }), +}); + +export type StdOutgoingListMessage = z.infer< + typeof stdOutgoingListMessageSchema +>; + +export const stdOutgoingAttachmentMessageSchema = z.object({ + attachment: attachmentPayloadSchema, + quickReplies: z.array(stdQuickReplySchema).optional(), +}); + +export type StdOutgoingAttachmentMessage = z.infer< + typeof stdOutgoingAttachmentMessageSchema +>; + +export const pluginNameSchema = z.object({ + name: z.string().regex(/-plugin$/) as z.ZodType, +}); + +export const stdPluginMessageSchema = z.object({ + plugin: pluginNameSchema, + args: z.record(z.any()), +}); + +export type StdPluginMessage = z.infer; + +export const BlockMessageSchema = z.union([ + z.array(z.string()), + stdOutgoingTextMessageSchema, + stdOutgoingQuickRepliesMessageSchema, + stdOutgoingButtonsMessageSchema, + stdOutgoingListMessageSchema, + stdOutgoingAttachmentMessageSchema, + stdPluginMessageSchema, +]); + +export type BlockMessage = z.infer; + +export const StdOutgoingMessageSchema = z.union([ + stdOutgoingTextMessageSchema, + stdOutgoingQuickRepliesMessageSchema, + stdOutgoingButtonsMessageSchema, + stdOutgoingListMessageSchema, + stdOutgoingAttachmentMessageSchema, +]); + +export type StdOutgoingMessage = z.infer; + +export const stdIncomingTextMessageSchema = z.object({ + text: z.string(), +}); + +export type StdIncomingTextMessage = z.infer< + typeof stdIncomingTextMessageSchema +>; + +export const stdIncomingPostBackMessageSchema = + stdIncomingTextMessageSchema.extend({ + postback: z.string(), + }); + +export type StdIncomingPostBackMessage = z.infer< + typeof stdIncomingPostBackMessageSchema +>; + +export const stdIncomingLocationMessageSchema = z.object({ + type: z.literal(PayloadType.location), + coordinates: z.object({ + lat: z.number(), + lon: z.number(), + }), +}); + +export type StdIncomingLocationMessage = z.infer< + typeof stdIncomingLocationMessageSchema +>; + +export const stdIncomingAttachmentMessageSchema = z.object({ + type: z.literal(PayloadType.attachments), + serialized_text: z.string(), + attachment: z.union([ + attachmentPayloadSchema, + z.array(attachmentPayloadSchema), + ]), +}); + +export type StdIncomingAttachmentMessage = z.infer< + typeof stdIncomingAttachmentMessageSchema +>; + +export const stdIncomingMessageSchema = z.union([ + stdIncomingTextMessageSchema, + stdIncomingPostBackMessageSchema, + stdIncomingLocationMessageSchema, + stdIncomingAttachmentMessageSchema, +]); + +export type StdIncomingMessage = z.infer; export interface IncomingMessage extends Omit { message: StdIncomingMessage; @@ -162,34 +252,121 @@ export interface OutgoingMessage extends Omit { export type AnyMessage = IncomingMessage | OutgoingMessage; -export interface StdOutgoingTextEnvelope { - format: OutgoingMessageFormat.text; - message: StdOutgoingTextMessage; -} +export const stdOutgoingTextEnvelopeSchema = z.object({ + format: z.literal(OutgoingMessageFormat.text), + message: stdOutgoingTextMessageSchema, +}); -export interface StdOutgoingQuickRepliesEnvelope { - format: OutgoingMessageFormat.quickReplies; - message: StdOutgoingQuickRepliesMessage; -} +export type StdOutgoingTextEnvelope = z.infer< + typeof stdOutgoingTextEnvelopeSchema +>; -export interface StdOutgoingButtonsEnvelope { - format: OutgoingMessageFormat.buttons; - message: StdOutgoingButtonsMessage; -} +export const stdOutgoingQuickRepliesEnvelopeSchema = z.object({ + format: z.literal(OutgoingMessageFormat.quickReplies), + message: stdOutgoingQuickRepliesMessageSchema, +}); -export interface StdOutgoingListEnvelope { - format: OutgoingMessageFormat.list | OutgoingMessageFormat.carousel; - message: StdOutgoingListMessage; -} +export type StdOutgoingQuickRepliesEnvelope = z.infer< + typeof stdOutgoingQuickRepliesEnvelopeSchema +>; -export interface StdOutgoingAttachmentEnvelope { - format: OutgoingMessageFormat.attachment; - message: StdOutgoingAttachmentMessage; -} +export const stdOutgoingButtonsEnvelopeSchema = z.object({ + format: z.literal(OutgoingMessageFormat.buttons), + message: stdOutgoingButtonsMessageSchema, +}); + +export type StdOutgoingButtonsEnvelope = z.infer< + typeof stdOutgoingButtonsEnvelopeSchema +>; + +export const stdOutgoingListEnvelopeSchema = z.object({ + format: z.union([ + z.literal(OutgoingMessageFormat.list), + z.literal(OutgoingMessageFormat.carousel), + ]), + message: stdOutgoingListMessageSchema, +}); + +export type StdOutgoingListEnvelope = z.infer< + typeof stdOutgoingListEnvelopeSchema +>; + +export const stdOutgoingAttachmentEnvelopeSchema = z.object({ + format: z.literal(OutgoingMessageFormat.attachment), + message: stdOutgoingAttachmentMessageSchema, +}); + +export type StdOutgoingAttachmentEnvelope = z.infer< + typeof stdOutgoingAttachmentEnvelopeSchema +>; -export type StdOutgoingEnvelope = - | StdOutgoingTextEnvelope - | StdOutgoingQuickRepliesEnvelope - | StdOutgoingButtonsEnvelope - | StdOutgoingListEnvelope - | StdOutgoingAttachmentEnvelope; +export const stdOutgoingEnvelopeSchema = z.union([ + stdOutgoingTextEnvelopeSchema, + stdOutgoingQuickRepliesEnvelopeSchema, + stdOutgoingButtonsEnvelopeSchema, + stdOutgoingListEnvelopeSchema, + stdOutgoingAttachmentEnvelopeSchema, +]); + +export type StdOutgoingEnvelope = z.infer; + +// is-valid-message-text validation +export const validMessageTextSchema = z.object({ + message: z.string(), +}); + +// is-message validation +const MESSAGE_REGEX = /^function \(context\) \{[^]+\}/; + +export const messageRegexSchema = z.string().regex(MESSAGE_REGEX); + +export const textSchema = z.array(z.string().max(1000)); + +const quickReplySchema = z + .object({ + content_type: z.nativeEnum(QuickReplyType), + title: z.string().max(20).optional(), + payload: z.string().max(1000).optional(), + }) + .superRefine((data, ctx) => { + // When content_type is 'text', title and payload are required. + if (data.content_type === QuickReplyType.text) { + if (data.title == null) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Title is required when content_type is 'text'", + path: ['title'], + }); + } + if (data.payload == null) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Payload is required when content_type is 'text'", + path: ['payload'], + }); + } + } + }); + +// Attachment Message Schema +export const objectSchema = z.object({ + text: z.string().max(1000).optional(), + attachment: z + .object({ + type: z.nativeEnum(FileType), + payload: z.object({ + url: z.string().url().optional(), + id: z.string().optional(), + }), + }) + .optional(), + elements: z.boolean().optional(), + cards: z + .object({ + default_action: buttonSchema, + buttons: z.array(buttonSchema).max(3), + }) + .optional(), + buttons: z.array(buttonSchema).max(3).optional(), + quickReplies: z.array(quickReplySchema).max(11).optional(), +}); diff --git a/api/src/chat/schemas/types/options.ts b/api/src/chat/schemas/types/options.ts index db1fe8b4a..79ddacf00 100644 --- a/api/src/chat/schemas/types/options.ts +++ b/api/src/chat/schemas/types/options.ts @@ -1,42 +1,47 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -import { Button } from './button'; +import { z } from 'zod'; + +import { buttonSchema } from './button'; import { OutgoingMessageFormat } from './message'; -export interface ContentOptions { - display: OutgoingMessageFormat.list | OutgoingMessageFormat.carousel; - fields: { - title: string; - subtitle: string | null; - image_url: string | null; - url?: string; - action_title?: string; - action_payload?: string; - }; - buttons: Button[]; - limit: number; - query?: any; // Waterline model criteria - entity?: string | number; // ContentTypeID - top_element_style?: 'large' | 'compact'; -} +export const contentOptionsSchema = z.object({ + display: z.nativeEnum(OutgoingMessageFormat), + fields: z.object({ + title: z.string(), + subtitle: z.string().nullable(), + image_url: z.string().nullable(), + url: z.string().optional(), + action_title: z.string().optional(), + action_payload: z.string().optional(), + }), + buttons: z.array(buttonSchema), + limit: z.number().finite(), + query: z.any().optional(), + entity: z.union([z.string(), z.number().finite()]).optional(), + top_element_style: z.enum(['large', 'compact']).optional(), +}); + +export type ContentOptions = z.infer; + +export const BlockOptionsSchema = z.object({ + typing: z.number().optional(), + content: contentOptionsSchema.optional(), + fallback: z + .object({ + active: z.boolean(), + message: z.array(z.string()), + max_attempts: z.number().finite(), + }) + .optional(), + assignTo: z.string().optional(), + effects: z.array(z.string()).optional(), +}); -export interface BlockOptions { - typing?: number; - // In case of carousel/list message - content?: ContentOptions; - // Only if the block has next blocks - fallback?: { - active: boolean; - message: string[]; - max_attempts: number; - }; - assignTo?: string; - // plugins effects - effects?: string[]; -} +export type BlockOptions = z.infer; diff --git a/api/src/chat/schemas/types/quick-reply.ts b/api/src/chat/schemas/types/quick-reply.ts index e58cea157..95862874b 100644 --- a/api/src/chat/schemas/types/quick-reply.ts +++ b/api/src/chat/schemas/types/quick-reply.ts @@ -9,7 +9,6 @@ import { z } from 'zod'; import { attachmentPayloadSchema } from './attachment'; -import { PayloadType } from './message'; export enum QuickReplyType { text = 'text', @@ -25,11 +24,11 @@ export const cordinatesSchema = z.object({ export const payloadSchema = z.discriminatedUnion('type', [ z.object({ - type: z.literal(PayloadType.location), + type: z.literal('location'), coordinates: cordinatesSchema, }), z.object({ - type: z.literal(PayloadType.attachments), + type: z.literal('attachments'), attachment: attachmentPayloadSchema, }), ]); diff --git a/api/src/chat/services/block.service.ts b/api/src/chat/services/block.service.ts index c7fedabf5..61c6b6043 100644 --- a/api/src/chat/services/block.service.ts +++ b/api/src/chat/services/block.service.ts @@ -6,6 +6,14 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ +/* + * Copyright © 2025 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + import { Injectable } from '@nestjs/common'; import { AttachmentService } from '@/attachment/services/attachment.service'; @@ -224,7 +232,8 @@ export class BlockService extends BaseService< } else if ( typeof pattern === 'object' && 'label' in pattern && - text.trim().toLowerCase() === pattern.label.toLowerCase() + text.trim().toLowerCase() === + (pattern.label as unknown as string).toLowerCase() ) { // Payload (quick reply) return [text]; @@ -568,15 +577,13 @@ export class BlockService extends BaseService< contentBlockOptions, skip, ); - - const envelope: StdOutgoingEnvelope = { + const envelope = { format: contentBlockOptions.display, message: { ...results, options: contentBlockOptions, }, - }; - + } as StdOutgoingEnvelope; return envelope; } catch (err) { this.logger.error( @@ -588,7 +595,7 @@ export class BlockService extends BaseService< } else if (blockMessage && 'plugin' in blockMessage) { const plugin = this.pluginService.findPlugin( PluginType.block, - blockMessage.plugin as PluginName, + blockMessage.plugin as unknown as PluginName, ); // Process custom plugin block try { diff --git a/api/src/chat/validation-rules/is-message.ts b/api/src/chat/validation-rules/is-message.ts index 67688d941..057c71cb0 100644 --- a/api/src/chat/validation-rules/is-message.ts +++ b/api/src/chat/validation-rules/is-message.ts @@ -12,103 +12,43 @@ import { ValidatorConstraint, ValidatorConstraintInterface, } from 'class-validator'; -import Joi from 'joi'; -import { BlockMessage } from '../schemas/types/message'; +import { + BlockMessage, + messageRegexSchema, + objectSchema, + textSchema, +} from '../schemas/types/message'; +/* eslint-disable no-console */ export function isValidMessage(msg: any) { if (typeof msg === 'string' && msg !== '') { - // Custom code - const MESSAGE_REGEX = /^function \(context\) \{[^]+\}/; - if (!MESSAGE_REGEX.test(msg)) { - // eslint-disable-next-line - console.error('Block Model : Invalid custom code.', msg); + const result = messageRegexSchema.safeParse(msg); + if (!result.success) { + console.error('Block Model: Invalid custom code.', result.error); return false; - } else { - return true; } + return true; } else if (Array.isArray(msg)) { - // Simple text message - const textSchema = Joi.array().items(Joi.string().max(1000).required()); - const textCheck = textSchema.validate(msg); - return !textCheck.error; - } else if (typeof msg === 'object') { + const result = textSchema.safeParse(msg); + if (!result.success) { + console.error('Block Model: Invalid text message array.', result.error); + } + return result.success; + } else if (typeof msg === 'object' && msg !== null) { if ('plugin' in msg) { return true; - } else { - const buttonsSchema = Joi.array().items( - Joi.object().keys({ - type: Joi.string().valid('postback', 'web_url').required(), - title: Joi.string().max(20), - payload: Joi.alternatives().conditional('type', { - is: 'postback', - then: Joi.string().max(1000).required(), - otherwise: Joi.forbidden(), - }), - url: Joi.alternatives().conditional('type', { - is: 'web_url', - then: Joi.string().uri(), - otherwise: Joi.forbidden(), - }), - messenger_extensions: Joi.alternatives().conditional('type', { - is: 'web_url', - then: Joi.boolean(), - otherwise: Joi.forbidden(), - }), - webview_height_ratio: Joi.alternatives().conditional('type', { - is: 'web_url', - then: Joi.string().valid('compact', 'tall', 'full'), - otherwise: Joi.forbidden(), - }), - }), - ); - // Attachment message - const objectSchema = Joi.object().keys({ - text: Joi.string().max(1000), - attachment: Joi.object().keys({ - type: Joi.string() - .valid('image', 'audio', 'video', 'file', 'unknown') - .required(), - payload: Joi.object().keys({ - url: Joi.string().uri(), - id: Joi.string().allow(null), - }), - }), - elements: Joi.boolean(), - cards: Joi.object().keys({ - default_action: buttonsSchema.max(1), - buttons: buttonsSchema.max(3), - }), - buttons: buttonsSchema.max(3), - quickReplies: Joi.array() - .items( - Joi.object().keys({ - content_type: Joi.string() - .valid('text', 'location', 'user_phone_number', 'user_email') - .required(), - title: Joi.alternatives().conditional('content_type', { - is: 'text', - then: Joi.string().max(20).required(), - }), - payload: Joi.alternatives().conditional('content_type', { - is: 'text', - then: Joi.string().max(1000).required(), - }), - }), - ) - .max(11), - }); - const objectCheck = objectSchema.validate(msg); - if (objectCheck.error) { - // eslint-disable-next-line - console.log('Message validation failed! ', objectCheck); - } - return !objectCheck.error; } - } else { - return false; + const result = objectSchema.safeParse(msg); + if (!result.success) { + console.error('Block Model: Object validation failed!', result.error); + } + return result.success; } + console.log('Validation reached default false'); + return false; } +/* eslint-enable no-console */ @ValidatorConstraint({ async: false }) export class MessageValidator implements ValidatorConstraintInterface { diff --git a/api/src/chat/validation-rules/is-valid-message-text.ts b/api/src/chat/validation-rules/is-valid-message-text.ts index 485e78bce..caf63f510 100644 --- a/api/src/chat/validation-rules/is-valid-message-text.ts +++ b/api/src/chat/validation-rules/is-valid-message-text.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. @@ -11,6 +11,7 @@ import { registerDecorator, ValidationOptions } from 'class-validator'; import { StdIncomingMessage, StdOutgoingTextMessage, + validMessageTextSchema, } from '../schemas/types/message'; export function IsValidMessageText(validationOptions?: ValidationOptions) { @@ -21,7 +22,7 @@ export function IsValidMessageText(validationOptions?: ValidationOptions) { options: validationOptions, validator: { validate(message: StdOutgoingTextMessage | StdIncomingMessage) { - return !!(message as StdOutgoingTextMessage).text; + return validMessageTextSchema.safeParse(message).success; }, }, }); diff --git a/api/src/i18n/services/translation.service.spec.ts b/api/src/i18n/services/translation.service.spec.ts index 508d544e6..144586fc7 100644 --- a/api/src/i18n/services/translation.service.spec.ts +++ b/api/src/i18n/services/translation.service.spec.ts @@ -9,6 +9,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter'; import { Test, TestingModule } from '@nestjs/testing'; +import { BlockMessage } from '@/chat/schemas/types/message'; import { I18nService } from '@/i18n/services/i18n.service'; import { BasePlugin } from '@/plugins/base-plugin.service'; import { PluginService } from '@/plugins/plugins.service'; @@ -154,7 +155,7 @@ describe('TranslationService', () => { model: 'String 1', context: ['String 2', 'String 3'], }, - }, + } as unknown as BlockMessage, options: {}, attachedBlock: null, }; diff --git a/api/src/i18n/services/translation.service.ts b/api/src/i18n/services/translation.service.ts index 93bea9af6..1881cd6b0 100644 --- a/api/src/i18n/services/translation.service.ts +++ b/api/src/i18n/services/translation.service.ts @@ -11,7 +11,7 @@ import { OnEvent } from '@nestjs/event-emitter'; import { I18nService } from '@/i18n/services/i18n.service'; import { PluginService } from '@/plugins/plugins.service'; -import { PluginType } from '@/plugins/types'; +import { PluginName, PluginType } from '@/plugins/types'; import { SettingService } from '@/setting/services/setting.service'; import { BaseService } from '@/utils/generics/base-service'; @@ -54,7 +54,7 @@ export class TranslationService extends BaseService { if ('plugin' in block.message) { const plugin = this.pluginService.getPlugin( PluginType.block, - block.message.plugin, + block.message.plugin as unknown as PluginName, ); // plugin From 183a24198ea2449c0b2b07b5836a4e845b9a8d29 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 11:09:48 +0100 Subject: [PATCH 02/14] fix: uninstall joi --- api/package-lock.json | 44 ------------------------------------------- api/package.json | 1 - 2 files changed, 45 deletions(-) diff --git a/api/package-lock.json b/api/package-lock.json index 404f3eafa..cd14a1767 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -35,7 +35,6 @@ "dotenv": "^16.3.1", "ejs": "^3.1.9", "express-session": "^1.17.3", - "joi": "^17.11.0", "module-alias": "^2.2.3", "mongoose": "^8.0.0", "mongoose-lean-defaults": "^2.2.1", @@ -3646,19 +3645,6 @@ "@nestjs/core": "^10.x" } }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -5329,24 +5315,6 @@ "url": "https://ko-fi.com/killymxi" } }, - "node_modules/@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -13186,18 +13154,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/joi": { - "version": "17.11.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", - "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", - "dependencies": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, "node_modules/js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", diff --git a/api/package.json b/api/package.json index 2cd82dbe3..6ffcd5ded 100644 --- a/api/package.json +++ b/api/package.json @@ -70,7 +70,6 @@ "dotenv": "^16.3.1", "ejs": "^3.1.9", "express-session": "^1.17.3", - "joi": "^17.11.0", "module-alias": "^2.2.3", "mongoose": "^8.0.0", "mongoose-lean-defaults": "^2.2.1", From 1b2687d84da7fec511ee1574b146b098872abbe2 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 15:34:19 +0100 Subject: [PATCH 03/14] fix: attachment validation bug --- api/src/chat/schemas/types/message.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/api/src/chat/schemas/types/message.ts b/api/src/chat/schemas/types/message.ts index 7cf43b0af..67e3dc0b8 100644 --- a/api/src/chat/schemas/types/message.ts +++ b/api/src/chat/schemas/types/message.ts @@ -354,10 +354,12 @@ export const objectSchema = z.object({ attachment: z .object({ type: z.nativeEnum(FileType), - payload: z.object({ - url: z.string().url().optional(), - id: z.string().optional(), - }), + payload: z + .object({ + url: z.string().url().optional(), + id: z.string().nullable().optional(), + }) + .optional(), }) .optional(), elements: z.boolean().optional(), From abb90fcd12f855d280935e59fb4effc37d21dc1c Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 15:54:02 +0100 Subject: [PATCH 04/14] fix: apply feedback --- api/src/chat/schemas/types/message.ts | 12 ++++++------ api/src/chat/validation-rules/is-message.ts | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/src/chat/schemas/types/message.ts b/api/src/chat/schemas/types/message.ts index 67e3dc0b8..f7b370dc0 100644 --- a/api/src/chat/schemas/types/message.ts +++ b/api/src/chat/schemas/types/message.ts @@ -348,17 +348,17 @@ const quickReplySchema = z } }); -// Attachment Message Schema -export const objectSchema = z.object({ +// BlockMessage Schema +export const blockMessageObjectSchema = z.object({ text: z.string().max(1000).optional(), attachment: z .object({ type: z.nativeEnum(FileType), payload: z - .object({ - url: z.string().url().optional(), - id: z.string().nullable().optional(), - }) + .union([ + z.object({ url: z.string().url() }), + z.object({ id: z.string().nullable() }), + ]) .optional(), }) .optional(), diff --git a/api/src/chat/validation-rules/is-message.ts b/api/src/chat/validation-rules/is-message.ts index 057c71cb0..5ab9c7056 100644 --- a/api/src/chat/validation-rules/is-message.ts +++ b/api/src/chat/validation-rules/is-message.ts @@ -15,8 +15,8 @@ import { import { BlockMessage, + blockMessageObjectSchema, messageRegexSchema, - objectSchema, textSchema, } from '../schemas/types/message'; @@ -39,7 +39,7 @@ export function isValidMessage(msg: any) { if ('plugin' in msg) { return true; } - const result = objectSchema.safeParse(msg); + const result = blockMessageObjectSchema.safeParse(msg); if (!result.success) { console.error('Block Model: Object validation failed!', result.error); } From ccd8d95f8fcd9424940d309307f591c50baae62b Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 16:13:14 +0100 Subject: [PATCH 05/14] fix: apply feedback --- api/src/chat/schemas/types/message.ts | 62 +++++++++++++++++---------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/api/src/chat/schemas/types/message.ts b/api/src/chat/schemas/types/message.ts index f7b370dc0..71877bc4e 100644 --- a/api/src/chat/schemas/types/message.ts +++ b/api/src/chat/schemas/types/message.ts @@ -348,27 +348,43 @@ const quickReplySchema = z } }); +// pluginBlockMessageObjectSchema in case of plugin +export const pluginBlockMessageObjectSchema = z + .record(z.any()) + .superRefine((data, ctx) => { + if (!('plugin' in data)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "The object must contain the 'plugin' attribute", + path: ['plugin'], + }); + } + }); + // BlockMessage Schema -export const blockMessageObjectSchema = z.object({ - text: z.string().max(1000).optional(), - attachment: z - .object({ - type: z.nativeEnum(FileType), - payload: z - .union([ - z.object({ url: z.string().url() }), - z.object({ id: z.string().nullable() }), - ]) - .optional(), - }) - .optional(), - elements: z.boolean().optional(), - cards: z - .object({ - default_action: buttonSchema, - buttons: z.array(buttonSchema).max(3), - }) - .optional(), - buttons: z.array(buttonSchema).max(3).optional(), - quickReplies: z.array(quickReplySchema).max(11).optional(), -}); +export const blockMessageObjectSchema = z.union([ + pluginBlockMessageObjectSchema, + z.object({ + text: z.string().max(1000).optional(), + attachment: z + .object({ + type: z.nativeEnum(FileType), + payload: z + .union([ + z.object({ url: z.string().url() }), + z.object({ id: z.string().nullable() }), + ]) + .optional(), + }) + .optional(), + elements: z.boolean().optional(), + cards: z + .object({ + default_action: buttonSchema, + buttons: z.array(buttonSchema).max(3), + }) + .optional(), + buttons: z.array(buttonSchema).max(3).optional(), + quickReplies: z.array(quickReplySchema).max(11).optional(), + }), +]); From 1337c8cb6b31bce6c0eb345a5fbc153282dece50 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 16:17:31 +0100 Subject: [PATCH 06/14] fix: remove if plugin in msg check --- api/src/chat/validation-rules/is-message.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/src/chat/validation-rules/is-message.ts b/api/src/chat/validation-rules/is-message.ts index 5ab9c7056..20fc2f2b4 100644 --- a/api/src/chat/validation-rules/is-message.ts +++ b/api/src/chat/validation-rules/is-message.ts @@ -36,9 +36,6 @@ export function isValidMessage(msg: any) { } return result.success; } else if (typeof msg === 'object' && msg !== null) { - if ('plugin' in msg) { - return true; - } const result = blockMessageObjectSchema.safeParse(msg); if (!result.success) { console.error('Block Model: Object validation failed!', result.error); From 6879ff77b553cf95b363bdc86844741debe4e5a0 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 17:13:59 +0100 Subject: [PATCH 07/14] fix: pluginNameSchema typing --- api/src/chat/schemas/types/message.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/chat/schemas/types/message.ts b/api/src/chat/schemas/types/message.ts index 71877bc4e..bcdbe0eb5 100644 --- a/api/src/chat/schemas/types/message.ts +++ b/api/src/chat/schemas/types/message.ts @@ -154,9 +154,9 @@ export type StdOutgoingAttachmentMessage = z.infer< typeof stdOutgoingAttachmentMessageSchema >; -export const pluginNameSchema = z.object({ - name: z.string().regex(/-plugin$/) as z.ZodType, -}); +export const pluginNameSchema = z + .string() + .regex(/-plugin$/) as z.ZodType; export const stdPluginMessageSchema = z.object({ plugin: pluginNameSchema, From c66b3c3f9d46440bd110079c933a6a78d509fc0b Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 17:15:42 +0100 Subject: [PATCH 08/14] fix: unnecessary type casting --- api/src/chat/services/block.service.ts | 3 +-- api/src/i18n/services/translation.service.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/api/src/chat/services/block.service.ts b/api/src/chat/services/block.service.ts index 61c6b6043..55b33fcd3 100644 --- a/api/src/chat/services/block.service.ts +++ b/api/src/chat/services/block.service.ts @@ -232,8 +232,7 @@ export class BlockService extends BaseService< } else if ( typeof pattern === 'object' && 'label' in pattern && - text.trim().toLowerCase() === - (pattern.label as unknown as string).toLowerCase() + text.trim().toLowerCase() === pattern.label.toLowerCase() ) { // Payload (quick reply) return [text]; diff --git a/api/src/i18n/services/translation.service.ts b/api/src/i18n/services/translation.service.ts index 1881cd6b0..93bea9af6 100644 --- a/api/src/i18n/services/translation.service.ts +++ b/api/src/i18n/services/translation.service.ts @@ -11,7 +11,7 @@ import { OnEvent } from '@nestjs/event-emitter'; import { I18nService } from '@/i18n/services/i18n.service'; import { PluginService } from '@/plugins/plugins.service'; -import { PluginName, PluginType } from '@/plugins/types'; +import { PluginType } from '@/plugins/types'; import { SettingService } from '@/setting/services/setting.service'; import { BaseService } from '@/utils/generics/base-service'; @@ -54,7 +54,7 @@ export class TranslationService extends BaseService { if ('plugin' in block.message) { const plugin = this.pluginService.getPlugin( PluginType.block, - block.message.plugin as unknown as PluginName, + block.message.plugin, ); // plugin From 5150b2e3a6c03658c2b04573abe326363a5162d7 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 17:30:55 +0100 Subject: [PATCH 09/14] fix: refactor blockMessageSchema --- api/src/chat/schemas/types/message.ts | 67 ++++++++++++--------- api/src/chat/validation-rules/is-message.ts | 4 +- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/api/src/chat/schemas/types/message.ts b/api/src/chat/schemas/types/message.ts index bcdbe0eb5..d0efb09de 100644 --- a/api/src/chat/schemas/types/message.ts +++ b/api/src/chat/schemas/types/message.ts @@ -348,8 +348,8 @@ const quickReplySchema = z } }); -// pluginBlockMessageObjectSchema in case of plugin -export const pluginBlockMessageObjectSchema = z +// pluginBlockMessageSchema in case of Plugin Block +export const pluginBlockMessageSchema = z .record(z.any()) .superRefine((data, ctx) => { if (!('plugin' in data)) { @@ -361,30 +361,43 @@ export const pluginBlockMessageObjectSchema = z } }); -// BlockMessage Schema -export const blockMessageObjectSchema = z.union([ - pluginBlockMessageObjectSchema, - z.object({ - text: z.string().max(1000).optional(), - attachment: z - .object({ - type: z.nativeEnum(FileType), - payload: z - .union([ - z.object({ url: z.string().url() }), - z.object({ id: z.string().nullable() }), - ]) - .optional(), - }) - .optional(), - elements: z.boolean().optional(), - cards: z - .object({ - default_action: buttonSchema, - buttons: z.array(buttonSchema).max(3), - }) - .optional(), - buttons: z.array(buttonSchema).max(3).optional(), - quickReplies: z.array(quickReplySchema).max(11).optional(), +// textBlockMessageSchema in case of Text Block +const textBlockMessageSchema = z.string().max(1000); + +const buttonMessageSchema = z.object({ + text: z.string(), + buttons: z.array(buttonSchema).max(3), +}); + +// quickReplyMessageSchema in case of QuickReply Block +const quickReplyMessageSchema = z.object({ + text: z.string(), + quickReplies: z.array(quickReplySchema).max(11).optional(), +}); + +// listBlockMessageSchema in case of List Block +const listBlockMessageSchema = z.object({ + elements: z.boolean(), +}); + +// attachmentBlockMessageSchema in case of Attachment Block +const attachmentBlockMessageSchema = z.object({ + text: z.string().max(1000).optional(), + attachment: z.object({ + type: z.nativeEnum(FileType), + payload: z.union([ + z.object({ url: z.string().url() }), + z.object({ id: z.string().nullable() }), + ]), }), +}); + +// BlockMessage Schema +export const blockMessageSchema = z.union([ + pluginBlockMessageSchema, + textBlockMessageSchema, + buttonMessageSchema, + quickReplyMessageSchema, + listBlockMessageSchema, + attachmentBlockMessageSchema, ]); diff --git a/api/src/chat/validation-rules/is-message.ts b/api/src/chat/validation-rules/is-message.ts index 20fc2f2b4..f08f75d3e 100644 --- a/api/src/chat/validation-rules/is-message.ts +++ b/api/src/chat/validation-rules/is-message.ts @@ -15,7 +15,7 @@ import { import { BlockMessage, - blockMessageObjectSchema, + blockMessageSchema, messageRegexSchema, textSchema, } from '../schemas/types/message'; @@ -36,7 +36,7 @@ export function isValidMessage(msg: any) { } return result.success; } else if (typeof msg === 'object' && msg !== null) { - const result = blockMessageObjectSchema.safeParse(msg); + const result = blockMessageSchema.safeParse(msg); if (!result.success) { console.error('Block Model: Object validation failed!', result.error); } From 7332f77624673de361df93f53c36ce18be30cc28 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 17:51:31 +0100 Subject: [PATCH 10/14] fix: apply feedback --- api/src/chat/schemas/types/message.ts | 6 +++--- api/src/chat/validation-rules/is-message.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/src/chat/schemas/types/message.ts b/api/src/chat/schemas/types/message.ts index d0efb09de..4cacffa73 100644 --- a/api/src/chat/schemas/types/message.ts +++ b/api/src/chat/schemas/types/message.ts @@ -165,7 +165,7 @@ export const stdPluginMessageSchema = z.object({ export type StdPluginMessage = z.infer; -export const BlockMessageSchema = z.union([ +export const blockMessageSchema = z.union([ z.array(z.string()), stdOutgoingTextMessageSchema, stdOutgoingQuickRepliesMessageSchema, @@ -175,7 +175,7 @@ export const BlockMessageSchema = z.union([ stdPluginMessageSchema, ]); -export type BlockMessage = z.infer; +export type BlockMessage = z.infer; export const StdOutgoingMessageSchema = z.union([ stdOutgoingTextMessageSchema, @@ -393,7 +393,7 @@ const attachmentBlockMessageSchema = z.object({ }); // BlockMessage Schema -export const blockMessageSchema = z.union([ +export const blockMessageObjectSchema = z.union([ pluginBlockMessageSchema, textBlockMessageSchema, buttonMessageSchema, diff --git a/api/src/chat/validation-rules/is-message.ts b/api/src/chat/validation-rules/is-message.ts index f08f75d3e..20fc2f2b4 100644 --- a/api/src/chat/validation-rules/is-message.ts +++ b/api/src/chat/validation-rules/is-message.ts @@ -15,7 +15,7 @@ import { import { BlockMessage, - blockMessageSchema, + blockMessageObjectSchema, messageRegexSchema, textSchema, } from '../schemas/types/message'; @@ -36,7 +36,7 @@ export function isValidMessage(msg: any) { } return result.success; } else if (typeof msg === 'object' && msg !== null) { - const result = blockMessageSchema.safeParse(msg); + const result = blockMessageObjectSchema.safeParse(msg); if (!result.success) { console.error('Block Model: Object validation failed!', result.error); } From 1f8285a09cb89fb1bba7a08f46a1b8151ebbf292 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Tue, 4 Feb 2025 17:54:16 +0100 Subject: [PATCH 11/14] fix: remove casting --- api/src/chat/services/block.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/chat/services/block.service.ts b/api/src/chat/services/block.service.ts index 55b33fcd3..b92d3d5e9 100644 --- a/api/src/chat/services/block.service.ts +++ b/api/src/chat/services/block.service.ts @@ -25,7 +25,7 @@ import { I18nService } from '@/i18n/services/i18n.service'; import { LanguageService } from '@/i18n/services/language.service'; import { LoggerService } from '@/logger/logger.service'; import { PluginService } from '@/plugins/plugins.service'; -import { PluginName, PluginType } from '@/plugins/types'; +import { PluginType } from '@/plugins/types'; import { SettingService } from '@/setting/services/setting.service'; import { BaseService } from '@/utils/generics/base-service'; import { getRandom } from '@/utils/helpers/safeRandom'; @@ -594,7 +594,7 @@ export class BlockService extends BaseService< } else if (blockMessage && 'plugin' in blockMessage) { const plugin = this.pluginService.findPlugin( PluginType.block, - blockMessage.plugin as unknown as PluginName, + blockMessage.plugin, ); // Process custom plugin block try { From 4e80a0a2152b2a5209f6a610a628a1a19466cd78 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Wed, 5 Feb 2025 11:41:09 +0100 Subject: [PATCH 12/14] fix: typing & remove type assertion --- api/src/chat/schemas/types/message.ts | 5 +---- api/src/chat/schemas/types/options.ts | 3 +-- api/src/chat/services/block.service.ts | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/api/src/chat/schemas/types/message.ts b/api/src/chat/schemas/types/message.ts index 4cacffa73..451d7db42 100644 --- a/api/src/chat/schemas/types/message.ts +++ b/api/src/chat/schemas/types/message.ts @@ -280,10 +280,7 @@ export type StdOutgoingButtonsEnvelope = z.infer< >; export const stdOutgoingListEnvelopeSchema = z.object({ - format: z.union([ - z.literal(OutgoingMessageFormat.list), - z.literal(OutgoingMessageFormat.carousel), - ]), + format: z.enum(['list', 'carousel']), message: stdOutgoingListMessageSchema, }); diff --git a/api/src/chat/schemas/types/options.ts b/api/src/chat/schemas/types/options.ts index 79ddacf00..f413a56de 100644 --- a/api/src/chat/schemas/types/options.ts +++ b/api/src/chat/schemas/types/options.ts @@ -9,10 +9,9 @@ import { z } from 'zod'; import { buttonSchema } from './button'; -import { OutgoingMessageFormat } from './message'; export const contentOptionsSchema = z.object({ - display: z.nativeEnum(OutgoingMessageFormat), + display: z.enum(['list', 'carousel']), fields: z.object({ title: z.string(), subtitle: z.string().nullable(), diff --git a/api/src/chat/services/block.service.ts b/api/src/chat/services/block.service.ts index b92d3d5e9..bba56b8a0 100644 --- a/api/src/chat/services/block.service.ts +++ b/api/src/chat/services/block.service.ts @@ -576,13 +576,13 @@ export class BlockService extends BaseService< contentBlockOptions, skip, ); - const envelope = { + const envelope: StdOutgoingEnvelope = { format: contentBlockOptions.display, message: { ...results, options: contentBlockOptions, }, - } as StdOutgoingEnvelope; + }; return envelope; } catch (err) { this.logger.error( From 87e5477e8e52a61e23a05ad21829498c33b7a4df Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Wed, 5 Feb 2025 11:55:28 +0100 Subject: [PATCH 13/14] fix: remove type assertion --- api/src/i18n/services/translation.service.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/src/i18n/services/translation.service.spec.ts b/api/src/i18n/services/translation.service.spec.ts index 144586fc7..508d544e6 100644 --- a/api/src/i18n/services/translation.service.spec.ts +++ b/api/src/i18n/services/translation.service.spec.ts @@ -9,7 +9,6 @@ import { EventEmitter2 } from '@nestjs/event-emitter'; import { Test, TestingModule } from '@nestjs/testing'; -import { BlockMessage } from '@/chat/schemas/types/message'; import { I18nService } from '@/i18n/services/i18n.service'; import { BasePlugin } from '@/plugins/base-plugin.service'; import { PluginService } from '@/plugins/plugins.service'; @@ -155,7 +154,7 @@ describe('TranslationService', () => { model: 'String 1', context: ['String 2', 'String 3'], }, - } as unknown as BlockMessage, + }, options: {}, attachedBlock: null, }; From b0c02334121c892b7fd941e1b3988bda83fe64e3 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Wed, 5 Feb 2025 12:18:08 +0100 Subject: [PATCH 14/14] fix: remove duplicate copyright --- api/src/chat/services/block.service.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/api/src/chat/services/block.service.ts b/api/src/chat/services/block.service.ts index bba56b8a0..bab883878 100644 --- a/api/src/chat/services/block.service.ts +++ b/api/src/chat/services/block.service.ts @@ -6,14 +6,6 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -/* - * Copyright © 2025 Hexastack. All rights reserved. - * - * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: - * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. - * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). - */ - import { Injectable } from '@nestjs/common'; import { AttachmentService } from '@/attachment/services/attachment.service';