Files
twenty/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-send-message.service.ts
T
neo773 7c8d362772 feat: IMAP Driver Integration (#12576)
### Added IMAP integration 

This PR adds support for connecting email accounts via IMAP protocol,
allowing users to sync their emails without OAuth.

#### DB Changes:
- Added customConnectionParams and connectionType fields to
ConnectedAccountWorkspaceEntity

#### UI:
- Added settings pages for creating and editing IMAP connections with
proper validation and connection testing.
- Implemented reconnection flows for handling permission issues.

#### Backend:
- Built ImapConnectionModule with corresponding resolver and service for
managing IMAP connections.
- Created MessagingIMAPDriverModule to handle IMAP client operations,
message fetching/parsing, and error handling.

#### Dependencies:
Integrated `imapflow` and `mailparser` libraries with their type
definitions to handle the IMAP protocol communication.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-06-29 21:32:15 +02:00

107 lines
3.5 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
import { z } from 'zod';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
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 { MicrosoftClientProvider } from 'src/modules/messaging/message-import-manager/drivers/microsoft/providers/microsoft-client.provider';
import { mimeEncode } from 'src/modules/messaging/message-import-manager/utils/mime-encode.util';
interface SendMessageInput {
body: string;
subject: string;
to: string;
}
@Injectable()
export class MessagingSendMessageService {
constructor(
private readonly gmailClientProvider: GmailClientProvider,
private readonly oAuth2ClientProvider: OAuth2ClientProvider,
private readonly microsoftClientProvider: MicrosoftClientProvider,
) {}
public async sendMessage(
sendMessageInput: SendMessageInput,
connectedAccount: ConnectedAccountWorkspaceEntity,
): Promise<void> {
switch (connectedAccount.provider) {
case ConnectedAccountProvider.GOOGLE: {
const gmailClient =
await this.gmailClientProvider.getGmailClient(connectedAccount);
const oAuth2Client =
await this.oAuth2ClientProvider.getOAuth2Client(connectedAccount);
const { data } = await oAuth2Client.userinfo.get();
const fromEmail = data.email;
const fromName = data.name;
const headers: string[] = [];
if (isDefined(fromName)) {
headers.push(`From: "${mimeEncode(fromName)}" <${fromEmail}>`);
}
headers.push(
`To: ${sendMessageInput.to}`,
`Subject: ${mimeEncode(sendMessageInput.subject)}`,
'MIME-Version: 1.0',
'Content-Type: text/plain; charset="UTF-8"',
'',
sendMessageInput.body,
);
const message = headers.join('\n');
const encodedMessage = Buffer.from(message).toString('base64');
await gmailClient.users.messages.send({
userId: 'me',
requestBody: {
raw: encodedMessage,
},
});
break;
}
case ConnectedAccountProvider.MICROSOFT: {
const microsoftClient =
await this.microsoftClientProvider.getMicrosoftClient(
connectedAccount,
);
const message = {
subject: sendMessageInput.subject,
body: {
contentType: 'Text',
content: sendMessageInput.body,
},
toRecipients: [{ emailAddress: { address: sendMessageInput.to } }],
};
const response = await microsoftClient
.api(`/me/messages`)
.post(message);
z.string().parse(response.id);
await microsoftClient.api(`/me/messages/${response.id}/send`).post({});
break;
}
case ConnectedAccountProvider.IMAP_SMTP_CALDAV: {
throw new Error('IMAP provider does not support sending messages');
}
default:
assertUnreachable(
connectedAccount.provider,
`Provider ${connectedAccount.provider} not supported for sending messages`,
);
}
}
}