Update sent folder regex (#14069)
This commit is contained in:
+3
-23
@@ -3,6 +3,8 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { isNumber } from 'class-validator';
|
||||
import { ListResponse, type ImapFlow } from 'imapflow';
|
||||
|
||||
import { getImapSentFolderCandidatesByRegex } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/get-sent-folder-candidates-by-regex.util';
|
||||
|
||||
/**
|
||||
* Service to find sent folder using IMAP special-use flags
|
||||
*
|
||||
@@ -89,7 +91,7 @@ export class ImapFindSentFolderService {
|
||||
client: ImapFlow,
|
||||
list: ListResponse[],
|
||||
): Promise<string | null> {
|
||||
const regexCandidateFolders = this.getSentFolderCandidatesByRegex(list);
|
||||
const regexCandidateFolders = getImapSentFolderCandidatesByRegex(list);
|
||||
|
||||
for (const folder of regexCandidateFolders) {
|
||||
const messageCount = await this.getFolderMessageCount(client, folder);
|
||||
@@ -112,28 +114,6 @@ export class ImapFindSentFolderService {
|
||||
return null;
|
||||
}
|
||||
|
||||
private getSentFolderCandidatesByRegex(list: ListResponse[]): string[] {
|
||||
// Comprehensive regex pattern for legacy IMAP servers
|
||||
// Source: https://imapsync.lamiral.info/FAQ.d/FAQ.Folders_Mapping.txt
|
||||
// Based on imapsync's regextrans2 examples (originally "Sent|Sent Messages|Gesendet")
|
||||
// Extended with additional common localizations for broader provider/language support
|
||||
const sentFolderPattern =
|
||||
/^(.*\/)?(sent|sent[\s_-]?(items|mail|messages|elements)?|envoy[éê]s?|[ée]l[ée]ments[\s_-]?envoy[éê]s|gesendet|gesendete[\s_-]?elemente|enviados?|elementos[\s_-]?enviados|itens[\s_-]?enviados|posta[\s_-]?inviata|inviati|보낸편지함|\[gmail\]\/sent[\s_-]?mail)$/i;
|
||||
|
||||
const regexCandidateFolders = [];
|
||||
|
||||
for (const folder of list) {
|
||||
if (sentFolderPattern.test(folder.path)) {
|
||||
this.logger.debug(
|
||||
`Found potential sent folder via pattern match: ${folder.path}`,
|
||||
);
|
||||
regexCandidateFolders.push(folder.path);
|
||||
}
|
||||
}
|
||||
|
||||
return regexCandidateFolders;
|
||||
}
|
||||
|
||||
private async getFolderMessageCount(
|
||||
client: ImapFlow,
|
||||
folderPath: string,
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
import { type ListResponse } from 'imapflow';
|
||||
|
||||
import { getImapSentFolderCandidatesByRegex } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/get-sent-folder-candidates-by-regex.util';
|
||||
|
||||
function makeList(paths: string[]): ListResponse[] {
|
||||
return paths.map((p) => ({ path: p }) as ListResponse);
|
||||
}
|
||||
|
||||
describe('getSentFolderCandidatesByRegex', () => {
|
||||
it('matches English variants', () => {
|
||||
const englishVariants = [
|
||||
'Sent',
|
||||
'Sent Items',
|
||||
'Sent Mail',
|
||||
'Sent Messages',
|
||||
'sent-elements',
|
||||
];
|
||||
const input = makeList(englishVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(englishVariants));
|
||||
});
|
||||
|
||||
it('matches French variants', () => {
|
||||
const frenchVariants = ['Envoyés', 'Éléments envoyés', 'Objets envoyés'];
|
||||
const input = makeList(frenchVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(frenchVariants));
|
||||
});
|
||||
|
||||
it('matches German variants', () => {
|
||||
const germanVariants = ['Gesendet', 'Gesendete Elemente'];
|
||||
const input = makeList(germanVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(germanVariants));
|
||||
});
|
||||
|
||||
it('matches Spanish variants', () => {
|
||||
const spanishVariants = ['Enviados', 'Elementos enviados'];
|
||||
const input = makeList(spanishVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(spanishVariants));
|
||||
});
|
||||
|
||||
it('matches Portuguese variants', () => {
|
||||
const portugueseVariants = ['Itens enviados'];
|
||||
const input = makeList(portugueseVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(portugueseVariants));
|
||||
});
|
||||
|
||||
it('matches Italian variants', () => {
|
||||
const italianVariants = ['Posta inviata', 'Inviati'];
|
||||
const input = makeList(italianVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(italianVariants));
|
||||
});
|
||||
|
||||
it('matches Korean variant', () => {
|
||||
const koreanVariants = ['보낸편지함'];
|
||||
const input = makeList(koreanVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(koreanVariants);
|
||||
});
|
||||
|
||||
it('matches Japanese variants', () => {
|
||||
const japaneseVariants = ['送信済みメール', '送信済み'];
|
||||
const input = makeList(japaneseVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(japaneseVariants));
|
||||
});
|
||||
|
||||
it('matches Polish variants', () => {
|
||||
const polishVariants = ['Wysłane', 'Elementy wysłane'];
|
||||
const input = makeList(polishVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(polishVariants));
|
||||
});
|
||||
|
||||
it('matches Russian variants', () => {
|
||||
const russianVariants = [
|
||||
'Отправленные',
|
||||
'Отправленные письма',
|
||||
'Отправленные сообщения',
|
||||
'Исходящие',
|
||||
];
|
||||
const input = makeList(russianVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(russianVariants));
|
||||
});
|
||||
|
||||
it('matches Gmail special folder', () => {
|
||||
const gmailVariants = ['[Gmail]/Sent Mail', '[Gmail]\\Sent Mail'];
|
||||
const input = makeList(gmailVariants);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(gmailVariants));
|
||||
});
|
||||
|
||||
it('does not match unrelated folders', () => {
|
||||
const unrelatedFolders = [
|
||||
'Inbox',
|
||||
'Drafts',
|
||||
'Trash',
|
||||
'Archive',
|
||||
'Junk',
|
||||
'Important',
|
||||
'RandomFolder',
|
||||
];
|
||||
const input = makeList(unrelatedFolders);
|
||||
const result = getImapSentFolderCandidatesByRegex(input);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { type ListResponse } from 'imapflow';
|
||||
|
||||
export function getImapSentFolderCandidatesByRegex(
|
||||
list: ListResponse[],
|
||||
): string[] {
|
||||
const sentFolderPattern = new RegExp(
|
||||
[
|
||||
// EN
|
||||
'sent([\\s_-]?(items|mail|messages|elements))?',
|
||||
// FR
|
||||
'envoy[éê]s?',
|
||||
'[ée]l[ée]ments[\\s_-]?envoy[éê]s',
|
||||
// DE
|
||||
'gesendet',
|
||||
'gesendete[\\s_-]?elemente',
|
||||
// ES
|
||||
'enviados?',
|
||||
'elementos[\\s_-]?enviados',
|
||||
// PT
|
||||
'itens[\\s_-]?enviados',
|
||||
// IT
|
||||
'posta[\\s_-]?inviata',
|
||||
'inviati',
|
||||
// KO
|
||||
'보낸편지함',
|
||||
// JA
|
||||
'送信済みメール',
|
||||
'送信済み',
|
||||
// PL
|
||||
'elementy[\\s_-]?wysłane',
|
||||
'wysłane',
|
||||
// RU
|
||||
'отправленные',
|
||||
'отправленные[\\s_-]?(сообщения|письма)?',
|
||||
'исходящие',
|
||||
// GMAIL
|
||||
'\\[gmail\\][\\/]+sent[\\s_-]?mail',
|
||||
]
|
||||
.map((s) => `(${s})`)
|
||||
.join('|'),
|
||||
'i',
|
||||
);
|
||||
|
||||
const regexCandidateFolders = [];
|
||||
|
||||
for (const folder of list) {
|
||||
if (sentFolderPattern.test(folder.path)) {
|
||||
regexCandidateFolders.push(folder.path);
|
||||
}
|
||||
}
|
||||
|
||||
return regexCandidateFolders;
|
||||
}
|
||||
Reference in New Issue
Block a user