hunk.code_window:         step: {
             digest: !!command.events?.length,
             events: command.events,
             total_count: command.events?.length,
           },
           ...(tenant ? { tenant: { name: tenant.name, ...tenant.data } } : {}),
           subscriber,
         },
       };
       const messagePayload = Object.assign({}, command.payload);
  
hunk.labels: keep keep keep keep keep replace keep keep keep keep keep
hunk.after_edit:         ...(tenant && { tenant }),
  
hunk.file_path: apps/worker/src/app/workflow/usecases/send-message/send-message-email.usecase.ts
hunk.type: replace
file: import { Injectable, Logger } from '@nestjs/common';
  import * as Sentry from '@sentry/node';
  import {
    NotificationStepEntity,
    SubscriberRepository,
    MessageRepository,
    MessageEntity,
    IntegrationEntity,
    IChannelSettings,
    TenantRepository,
  } from '@novu/dal';
  import {
    ChannelTypeEnum,
    LogCodeEnum,
    ChatProviderIdEnum,
    ExecutionDetailsSourceEnum,
    ExecutionDetailsStatusEnum,
  } from '@novu/shared';
  import {
    InstrumentUsecase,
    DetailEnum,
    CreateExecutionDetails,
    CreateExecutionDetailsCommand,
    CompileTemplate,
    CompileTemplateCommand,
    ChatFactory,
    SelectIntegration,
    GetNovuProviderCredentials,
  } from '@novu/application-generic';
  
  import { CreateLog } from '../../../shared/logs';
  import { SendMessageCommand } from './send-message.command';
  import { SendMessageBase } from './send-message.base';
  import { PlatformException } from '../../../shared/utils';
  
  const LOG_CONTEXT = 'SendMessageChat';
  
  @Injectable()
  export class SendMessageChat extends SendMessageBase {
    channelType = ChannelTypeEnum.CHAT;
  
    constructor(
      protected subscriberRepository: SubscriberRepository,
      protected messageRepository: MessageRepository,
      protected tenantRepository: TenantRepository,
      protected createLogUsecase: CreateLog,
      protected createExecutionDetails: CreateExecutionDetails,
      private compileTemplate: CompileTemplate,
      protected selectIntegration: SelectIntegration,
      protected getNovuProviderCredentials: GetNovuProviderCredentials
    ) {
      super(
        messageRepository,
        createLogUsecase,
        createExecutionDetails,
        subscriberRepository,
        tenantRepository,
        selectIntegration,
        getNovuProviderCredentials
      );
    }
  
    @InstrumentUsecase()
    public async execute(command: SendMessageCommand) {
      const subscriber = await this.getSubscriberBySubscriberId({
        subscriberId: command.subscriberId,
        _environmentId: command.environmentId,
      });
      if (!subscriber) throw new PlatformException('Subscriber not found');
  
      Sentry.addBreadcrumb({
        message: 'Sending Chat',
      });
      const chatChannel: NotificationStepEntity = command.step;
      if (!chatChannel?.template) throw new PlatformException('Chat channel template not found');
  
      const tenant = await this.handleTenantExecution(command.job);
  
      let content = '';
      const data = {
        subscriber: subscriber,
        step: {
          digest: !!command.events?.length,
          events: command.events,
          total_count: command.events?.length,
        },
        ...(tenant ? { tenant: { name: tenant.name, ...tenant.data } } : {}),
        ...command.payload,
      };
  
      try {
        content = await this.compileTemplate.execute(
          CompileTemplateCommand.create({
            template: chatChannel.template.content as string,
            data,
          })
        );
      } catch (e) {
        await this.sendErrorHandlebars(command.job, e.message);
  
        return;
      }
  
      const chatChannels =
        subscriber.channels?.filter((chan) =>
          Object.values(ChatProviderIdEnum).includes(chan.providerId as ChatProviderIdEnum)
        ) || [];
  
      if (chatChannels.length === 0) {
        await this.createExecutionDetails.execute(
          CreateExecutionDetailsCommand.create({
            ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job),
            detail: DetailEnum.SUBSCRIBER_NO_ACTIVE_CHANNEL,
            source: ExecutionDetailsSourceEnum.INTERNAL,
            status: ExecutionDetailsStatusEnum.FAILED,
            isTest: false,
            isRetry: false,
          })
        );
  
        return;
      }
  
      let allFailed = true;
      for (const channel of chatChannels) {
        try {
          await this.sendChannelMessage(command, channel, chatChannel, content);
          allFailed = false;
        } catch (e) {
          /*
           * Do nothing, one chat channel failed, perhaps another one succeeds
           * The failed message has been created
           */
          Logger.error(e, `Sending chat message to the chat channel ${channel.providerId} failed`, LOG_CONTEXT);
        }
      }
  
      if (allFailed) {
        await this.createExecutionDetails.execute(
          CreateExecutionDetailsCommand.create({
            ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job),
            detail: DetailEnum.CHAT_ALL_CHANNELS_FAILED,
            source: ExecutionDetailsSourceEnum.INTERNAL,
            status: ExecutionDetailsStatusEnum.FAILED,
            isTest: false,
            isRetry: false,
          })
        );
      }
    }
  
    private async sendChannelMessage(
      command: SendMessageCommand,
      subscriberChannel: IChannelSettings,
      chatChannel: NotificationStepEntity,
      content: string
    ) {
      const integration = await this.getIntegration({
        id: subscriberChannel._integrationId,
        organizationId: command.organizationId,
        environmentId: command.environmentId,
        providerId: subscriberChannel.providerId,
        channelType: ChannelTypeEnum.CHAT,
        userId: command.userId,
      });
  
      const chatWebhookUrl = command.payload.webhookUrl || subscriberChannel.credentials?.webhookUrl;
      const channelSpecification = subscriberChannel.credentials?.channel;
  
      if (!chatWebhookUrl) {
        await this.createExecutionDetails.execute(
          CreateExecutionDetailsCommand.create({
            ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job),
            detail: DetailEnum.CHAT_WEBHOOK_URL_MISSING,
            source: ExecutionDetailsSourceEnum.INTERNAL,
            status: ExecutionDetailsStatusEnum.FAILED,
            isTest: false,
            isRetry: false,
          })
        );
      }
  
      const message: MessageEntity = await this.messageRepository.create({
        _notificationId: command.notificationId,
        _environmentId: command.environmentId,
        _organizationId: command.organizationId,
        _subscriberId: command._subscriberId,
        _templateId: command._templateId,
        _messageTemplateId: chatChannel.template?._id,
        channel: ChannelTypeEnum.CHAT,
        transactionId: command.transactionId,
        chatWebhookUrl: chatWebhookUrl,
        content: this.storeContent() ? content : null,
        providerId: subscriberChannel.providerId,
        _jobId: command.jobId,
      });
  
      if (!integration) {
        await this.createExecutionDetails.execute(
          CreateExecutionDetailsCommand.create({
            ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job),
            detail: DetailEnum.SUBSCRIBER_NO_ACTIVE_INTEGRATION,
            source: ExecutionDetailsSourceEnum.INTERNAL,
            status: ExecutionDetailsStatusEnum.FAILED,
            isTest: false,
            isRetry: false,
          })
        );
  
        return;
      }
  
      await this.sendSelectedIntegrationExecution(command.job, integration);
  
      await this.createExecutionDetails.execute(
        CreateExecutionDetailsCommand.create({
          ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job),
          detail: DetailEnum.MESSAGE_CREATED,
          source: ExecutionDetailsSourceEnum.INTERNAL,
          status: ExecutionDetailsStatusEnum.PENDING,
          messageId: message._id,
          isTest: false,
          isRetry: false,
          raw: this.storeContent() ? JSON.stringify(content) : null,
        })
      );
  
      if (chatWebhookUrl && integration) {
        await this.sendMessage(chatWebhookUrl, integration, content, message, command, channelSpecification);
  
        return;
      }
  
      await this.sendErrors(chatWebhookUrl, integration, message, command);
    }
  
    private async sendErrors(
      chatWebhookUrl,
      integration: IntegrationEntity,
      message: MessageEntity,
      command: SendMessageCommand
    ) {
      if (!chatWebhookUrl) {
        await this.messageRepository.updateMessageStatus(
          command.environmentId,
          message._id,
          'warning',
          null,
          'no_subscriber_chat_channel_id',
          'Subscriber does not have active chat channel id'
        );
  
        await this.createExecutionDetails.execute(
          CreateExecutionDetailsCommand.create({
            ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job),
            messageId: message._id,
            detail: DetailEnum.SUBSCRIBER_NO_ACTIVE_CHANNEL,
            source: ExecutionDetailsSourceEnum.INTERNAL,
            status: ExecutionDetailsStatusEnum.FAILED,
            isTest: false,
            isRetry: false,
          })
        );
  
        return;
      }
      if (!integration) {
        await this.sendErrorStatus(
          message,
          'warning',
          'chat_missing_integration_error',
          'Subscriber does not have an active chat integration',
          command,
          LogCodeEnum.MISSING_CHAT_INTEGRATION
        );
        await this.createExecutionDetails.execute(
          CreateExecutionDetailsCommand.create({
            ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job),
            messageId: message._id,
            detail: DetailEnum.SUBSCRIBER_NO_ACTIVE_INTEGRATION,
            source: ExecutionDetailsSourceEnum.INTERNAL,
            status: ExecutionDetailsStatusEnum.FAILED,
            isTest: false,
            isRetry: false,
          })
        );
  
        return;
      }
    }
  
    private async sendMessage(
      chatWebhookUrl: string,
      integration: IntegrationEntity,
      content: string,
      message: MessageEntity,
      command: SendMessageCommand,
      channelSpecification?: string | undefined
    ) {
      try {
        const chatFactory = new ChatFactory();
        const chatHandler = chatFactory.getHandler(integration);
        if (!chatHandler) {
          throw new PlatformException(`Chat handler for provider ${integration.providerId} is  not found`);
        }
  
        const result = await chatHandler.send({
          webhookUrl: chatWebhookUrl,
          channel: channelSpecification,
          content,
        });
  
        await this.createExecutionDetails.execute(
          CreateExecutionDetailsCommand.create({
            ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job),
            messageId: message._id,
            detail: DetailEnum.MESSAGE_SENT,
            source: ExecutionDetailsSourceEnum.INTERNAL,
            status: ExecutionDetailsStatusEnum.SUCCESS,
            isTest: false,
            isRetry: false,
            raw: JSON.stringify(result),
          })
        );
      } catch (e) {
        await this.sendErrorStatus(
          message,
          'error',
          'unexpected_chat_error',
          e.message || e.name || 'Un-expect CHAT provider error',
          command,
          LogCodeEnum.CHAT_ERROR,
          e
        );
  
        await this.createExecutionDetails.execute(
          CreateExecutionDetailsCommand.create({
            ...CreateExecutionDetailsCommand.getDetailsFromJob(command.job),
            messageId: message._id,
            detail: DetailEnum.PROVIDER_ERROR,
            source: ExecutionDetailsSourceEnum.INTERNAL,
            status: ExecutionDetailsStatusEnum.FAILED,
            isTest: false,
            isRetry: false,
            raw: JSON.stringify(e),
          })
        );
      }
    }
  }
  