Integrate IMAP and SMTP clients for message sending (#14688)

Implement functionality to send messages using both IMAP and SMTP
clients, allowing for message storage in the Sent folder.

Closes #14379

---------

Co-authored-by: neo773 <neo773@protonmail.com>
Co-authored-by: neo773 <62795688+neo773@users.noreply.github.com>
This commit is contained in:
Abdul Mateen
2025-10-06 21:41:18 +05:00
committed by GitHub
parent e54aa9d925
commit a3a25aaae5
3 changed files with 49 additions and 4 deletions
@@ -50,8 +50,14 @@ export class SendEmailTool implements Tool {
workspaceId,
'connectedAccount',
);
const connectedAccount = await connectedAccountRepository.findOneBy({
id: connectedAccountId,
const connectedAccount = await connectedAccountRepository.findOne({
where: { id: connectedAccountId },
relations: {
messageChannels: {
messageFolders: true,
},
},
});
if (!isDefined(connectedAccount)) {
@@ -4,6 +4,7 @@ import { ConnectedAccountProvider } from 'twenty-shared/types';
import { GmailClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/gmail-client.provider';
import { OAuth2ClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/oauth2-client.provider';
import { ImapClientProvider } from 'src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider';
import { MicrosoftClientProvider } from 'src/modules/messaging/message-import-manager/drivers/microsoft/providers/microsoft-client.provider';
import { SmtpClientProvider } from 'src/modules/messaging/message-import-manager/drivers/smtp/providers/smtp-client.provider';
import { MessagingSendMessageService } from 'src/modules/messaging/message-import-manager/services/messaging-send-message.service';
@@ -53,6 +54,10 @@ describe('MessagingSendMessageService - Gmail HTML Support', () => {
provide: SmtpClientProvider,
useValue: {},
},
{
provide: ImapClientProvider,
useValue: {},
},
],
}).compile();
@@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import MailComposer from 'nodemailer/lib/mail-composer';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
import { z } from 'zod';
@@ -11,6 +12,7 @@ import {
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import { GmailClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/gmail-client.provider';
import { OAuth2ClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/oauth2-client.provider';
import { ImapClientProvider } from 'src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider';
import { MicrosoftClientProvider } from 'src/modules/messaging/message-import-manager/drivers/microsoft/providers/microsoft-client.provider';
import { isAccessTokenRefreshingError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/is-access-token-refreshing-error.utils';
import { SmtpClientProvider } from 'src/modules/messaging/message-import-manager/drivers/smtp/providers/smtp-client.provider';
@@ -30,6 +32,7 @@ export class MessagingSendMessageService {
private readonly oAuth2ClientProvider: OAuth2ClientProvider,
private readonly microsoftClientProvider: MicrosoftClientProvider,
private readonly smtpClientProvider: SmtpClientProvider,
private readonly imapClientProvider: ImapClientProvider,
) {}
public async sendMessage(
@@ -134,16 +137,47 @@ export class MessagingSendMessageService {
break;
}
case ConnectedAccountProvider.IMAP_SMTP_CALDAV: {
const { handle, connectionParameters, messageChannels } =
connectedAccount;
const smtpClient =
await this.smtpClientProvider.getSmtpClient(connectedAccount);
await smtpClient.sendMail({
from: connectedAccount.handle,
const mail = new MailComposer({
from: handle,
to: sendMessageInput.to,
subject: sendMessageInput.subject,
text: sendMessageInput.body,
html: sendMessageInput.html,
});
const messageBuffer = await mail.compile().build();
await smtpClient.sendMail({
from: handle,
to: sendMessageInput.to,
raw: messageBuffer,
});
if (isDefined(connectionParameters?.IMAP)) {
const imapClient =
await this.imapClientProvider.getClient(connectedAccount);
const messageChannel = messageChannels.find(
(channel) => channel.handle === handle,
);
const sentFolder = messageChannel?.messageFolders.find(
(messageFolder) => messageFolder.isSentFolder,
);
if (isDefined(sentFolder)) {
await imapClient.append(sentFolder.name, messageBuffer);
}
await this.imapClientProvider.closeClient(imapClient);
}
break;
}
default: