chore: add ai service
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { AiService } from "./ai.service";
|
||||
import { AiMessage } from "../../../common/enums/message.enum";
|
||||
import { MessageDetails } from "../../mail-server/interfaces/messages-response.interface";
|
||||
import { WildDuckMessagesService } from "../../mail-server/services/wildduck-messages.service";
|
||||
import { SummarizeEmailDto } from "../DTO/summarize-email.dto";
|
||||
import { WriteEmailAssistanceDto } from "../DTO/write-email-assistance.dto";
|
||||
import { SummaryLength, SummaryTone } from "../enums/ai.enum";
|
||||
import { EmailData, SummarizationOptions, WritingAssistanceOptions } from "../interfaces/ai-service.interface";
|
||||
|
||||
@Injectable()
|
||||
export class AiAssistantService {
|
||||
constructor(
|
||||
private readonly messagesService: WildDuckMessagesService,
|
||||
private readonly aiService: AiService,
|
||||
) {}
|
||||
|
||||
//************************************************* */
|
||||
async summarizeEmail(summarizeDto: SummarizeEmailDto, userId: string) {
|
||||
const message = await firstValueFrom(this.messagesService.getMessage(userId, summarizeDto.mailbox, summarizeDto.messageId));
|
||||
|
||||
if (!message) throw new NotFoundException(AiMessage.MESSAGE_NOT_FOUND_OR_ACCESS_DENIED);
|
||||
|
||||
const emailData = this.extractEmailData(message);
|
||||
|
||||
const summary = await this.generateEmailSummary(emailData, summarizeDto.length, summarizeDto.tone);
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
//************************************************* */
|
||||
async assistEmailWriting(writeAssistanceDto: WriteEmailAssistanceDto, userId: string) {
|
||||
const assistance = await this.generateEmailWritingAssistance(writeAssistanceDto, userId);
|
||||
|
||||
return assistance;
|
||||
}
|
||||
|
||||
//************************************************* */
|
||||
private extractEmailData(message: MessageDetails) {
|
||||
return {
|
||||
subject: message.subject,
|
||||
content: this.extractTextContent(message),
|
||||
htmlContent: this.extractHtmlContent(message),
|
||||
from: this.extractSenderInfo(message),
|
||||
to: this.extractRecipientInfo(message),
|
||||
date: message.date || new Date(),
|
||||
attachments: message.attachments || [],
|
||||
};
|
||||
}
|
||||
|
||||
//************************************************* */
|
||||
private extractTextContent(message: MessageDetails): string {
|
||||
if (message.text) {
|
||||
return typeof message.text === "string" ? message.text : message.text || "";
|
||||
}
|
||||
|
||||
if (message.html) {
|
||||
return this.stripHtmlTags(typeof message.html === "string" ? message.html : message.html[0] || "");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
//************************************************* */
|
||||
private extractHtmlContent(message: MessageDetails): string {
|
||||
if (message.html) {
|
||||
return typeof message.html === "string" ? message.html : message.html[0] || "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//************************************************* */
|
||||
private extractSenderInfo(message: MessageDetails): string {
|
||||
if (message.from && message.from.address) {
|
||||
return message.from.name ? `${message.from.name} <${message.from.address}>` : message.from.address;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//************************************************* */
|
||||
private extractRecipientInfo(message: MessageDetails): string[] {
|
||||
const recipients: string[] = [];
|
||||
|
||||
if (message.to && Array.isArray(message.to)) {
|
||||
recipients.push(...message.to.map((recipient) => (recipient.name ? `${recipient.name} <${recipient.address}>` : recipient.address)));
|
||||
}
|
||||
|
||||
return recipients;
|
||||
}
|
||||
|
||||
//************************************************* */
|
||||
private stripHtmlTags(html: string): string {
|
||||
return html
|
||||
.replace(/<[^>]*>/g, "")
|
||||
.replace(/ /g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
//************************************************* */
|
||||
private async generateEmailSummary(emailData: any, length: SummaryLength = SummaryLength.MEDIUM, _tone: SummaryTone = SummaryTone.PROFESSIONAL) {
|
||||
const content = emailData.content || emailData.htmlContent;
|
||||
|
||||
if (!content.trim()) throw new BadRequestException(AiMessage.EMAIL_CONTENT_IS_EMPTY_OR_COULD_NOT_BE_EXTRACTED);
|
||||
|
||||
const aiEmailData: EmailData = {
|
||||
subject: emailData.subject,
|
||||
content: emailData.content,
|
||||
htmlContent: emailData.htmlContent,
|
||||
from: emailData.from,
|
||||
to: emailData.to,
|
||||
date: emailData.date,
|
||||
attachments: emailData.attachments,
|
||||
};
|
||||
|
||||
const summarizationOptions: SummarizationOptions = {
|
||||
length: length.toLowerCase() as "short" | "medium" | "detailed",
|
||||
tone: _tone.toLowerCase() as "formal" | "casual" | "professional",
|
||||
};
|
||||
|
||||
const aiResponse = await this.aiService.summarizeEmail(aiEmailData, summarizationOptions);
|
||||
|
||||
return {
|
||||
summary: aiResponse.summary,
|
||||
keyPoints: aiResponse.keyPoints,
|
||||
suggestedActions: aiResponse.suggestedActions,
|
||||
priority: aiResponse.priority,
|
||||
sentiment: aiResponse.sentiment,
|
||||
};
|
||||
}
|
||||
|
||||
//************************************************* */
|
||||
private async generateEmailWritingAssistance(writeAssistanceDto: WriteEmailAssistanceDto, _userId: string) {
|
||||
const writingOptions: WritingAssistanceOptions = {
|
||||
purpose: writeAssistanceDto.purpose.toLowerCase() as "reply" | "forward" | "new" | "follow_up",
|
||||
tone: (writeAssistanceDto.tone?.toLowerCase() as "formal" | "casual" | "professional" | "friendly" | "urgent") || "professional",
|
||||
length: (writeAssistanceDto.length?.toLowerCase() as "short" | "medium" | "long") || "medium",
|
||||
keyPoints: writeAssistanceDto.keyPoints,
|
||||
context: writeAssistanceDto.context
|
||||
? {
|
||||
originalContent: writeAssistanceDto.context.originalContent,
|
||||
originalSender: writeAssistanceDto.context.originalSender,
|
||||
originalSubject: writeAssistanceDto.context.originalSubject,
|
||||
}
|
||||
: undefined,
|
||||
additionalInstructions: writeAssistanceDto.additionalInstructions,
|
||||
};
|
||||
|
||||
const aiResponse = await this.aiService.assistEmailWriting(writeAssistanceDto.mainMessage, writingOptions);
|
||||
|
||||
return {
|
||||
suggestion: {
|
||||
subject: aiResponse.subject,
|
||||
content: aiResponse.content,
|
||||
alternatives: aiResponse.alternatives,
|
||||
},
|
||||
tips: aiResponse.tips,
|
||||
improvements: aiResponse.improvements,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user